polymo 0.1.0__py3-none-any.whl

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,335 @@
1
+ /* eslint-disable */
2
+ /* tslint:disable */
3
+
4
+ /**
5
+ * Mock Service Worker.
6
+ * @see https://github.com/mswjs/msw
7
+ * - Please do NOT modify this file.
8
+ */
9
+
10
+ const PACKAGE_VERSION = "2.11.3";
11
+ const INTEGRITY_CHECKSUM = "4db4a41e972cec1b64cc569c66952d82";
12
+ const IS_MOCKED_RESPONSE = Symbol("isMockedResponse");
13
+ const activeClientIds = new Set();
14
+
15
+ addEventListener("install", function () {
16
+ self.skipWaiting();
17
+ });
18
+
19
+ addEventListener("activate", function (event) {
20
+ event.waitUntil(self.clients.claim());
21
+ });
22
+
23
+ addEventListener("message", async function (event) {
24
+ const clientId = Reflect.get(event.source || {}, "id");
25
+
26
+ if (!clientId || !self.clients) {
27
+ return;
28
+ }
29
+
30
+ const client = await self.clients.get(clientId);
31
+
32
+ if (!client) {
33
+ return;
34
+ }
35
+
36
+ const allClients = await self.clients.matchAll({
37
+ type: "window",
38
+ });
39
+
40
+ switch (event.data) {
41
+ case "KEEPALIVE_REQUEST": {
42
+ sendToClient(client, {
43
+ type: "KEEPALIVE_RESPONSE",
44
+ });
45
+ break;
46
+ }
47
+
48
+ case "INTEGRITY_CHECK_REQUEST": {
49
+ sendToClient(client, {
50
+ type: "INTEGRITY_CHECK_RESPONSE",
51
+ payload: {
52
+ packageVersion: PACKAGE_VERSION,
53
+ checksum: INTEGRITY_CHECKSUM,
54
+ },
55
+ });
56
+ break;
57
+ }
58
+
59
+ case "MOCK_ACTIVATE": {
60
+ activeClientIds.add(clientId);
61
+
62
+ sendToClient(client, {
63
+ type: "MOCKING_ENABLED",
64
+ payload: {
65
+ client: {
66
+ id: client.id,
67
+ frameType: client.frameType,
68
+ },
69
+ },
70
+ });
71
+ break;
72
+ }
73
+
74
+ case "CLIENT_CLOSED": {
75
+ activeClientIds.delete(clientId);
76
+
77
+ const remainingClients = allClients.filter((client) => {
78
+ return client.id !== clientId;
79
+ });
80
+
81
+ // Unregister itself when there are no more clients
82
+ if (remainingClients.length === 0) {
83
+ self.registration.unregister();
84
+ }
85
+
86
+ break;
87
+ }
88
+ }
89
+ });
90
+
91
+ addEventListener("fetch", function (event) {
92
+ const requestInterceptedAt = Date.now();
93
+
94
+ // Bypass navigation requests.
95
+ if (event.request.mode === "navigate") {
96
+ return;
97
+ }
98
+
99
+ // Opening the DevTools triggers the "only-if-cached" request
100
+ // that cannot be handled by the worker. Bypass such requests.
101
+ if (event.request.cache === "only-if-cached" && event.request.mode !== "same-origin") {
102
+ return;
103
+ }
104
+
105
+ // Bypass all requests when there are no active clients.
106
+ // Prevents the self-unregistered worked from handling requests
107
+ // after it's been terminated (still remains active until the next reload).
108
+ if (activeClientIds.size === 0) {
109
+ return;
110
+ }
111
+
112
+ const requestId = crypto.randomUUID();
113
+ event.respondWith(handleRequest(event, requestId, requestInterceptedAt));
114
+ });
115
+
116
+ /**
117
+ * @param {FetchEvent} event
118
+ * @param {string} requestId
119
+ * @param {number} requestInterceptedAt
120
+ */
121
+ async function handleRequest(event, requestId, requestInterceptedAt) {
122
+ const client = await resolveMainClient(event);
123
+ const requestCloneForEvents = event.request.clone();
124
+ const response = await getResponse(event, client, requestId, requestInterceptedAt);
125
+
126
+ // Send back the response clone for the "response:*" life-cycle events.
127
+ // Ensure MSW is active and ready to handle the message, otherwise
128
+ // this message will pend indefinitely.
129
+ if (client && activeClientIds.has(client.id)) {
130
+ const serializedRequest = await serializeRequest(requestCloneForEvents);
131
+
132
+ // Clone the response so both the client and the library could consume it.
133
+ const responseClone = response.clone();
134
+
135
+ sendToClient(
136
+ client,
137
+ {
138
+ type: "RESPONSE",
139
+ payload: {
140
+ isMockedResponse: IS_MOCKED_RESPONSE in response,
141
+ request: {
142
+ id: requestId,
143
+ ...serializedRequest,
144
+ },
145
+ response: {
146
+ type: responseClone.type,
147
+ status: responseClone.status,
148
+ statusText: responseClone.statusText,
149
+ headers: Object.fromEntries(responseClone.headers.entries()),
150
+ body: responseClone.body,
151
+ },
152
+ },
153
+ },
154
+ responseClone.body ? [serializedRequest.body, responseClone.body] : [],
155
+ );
156
+ }
157
+
158
+ return response;
159
+ }
160
+
161
+ /**
162
+ * Resolve the main client for the given event.
163
+ * Client that issues a request doesn't necessarily equal the client
164
+ * that registered the worker. It's with the latter the worker should
165
+ * communicate with during the response resolving phase.
166
+ * @param {FetchEvent} event
167
+ * @returns {Promise<Client | undefined>}
168
+ */
169
+ async function resolveMainClient(event) {
170
+ const client = await self.clients.get(event.clientId);
171
+
172
+ if (activeClientIds.has(event.clientId)) {
173
+ return client;
174
+ }
175
+
176
+ if (client?.frameType === "top-level") {
177
+ return client;
178
+ }
179
+
180
+ const allClients = await self.clients.matchAll({
181
+ type: "window",
182
+ });
183
+
184
+ return allClients
185
+ .filter((client) => {
186
+ // Get only those clients that are currently visible.
187
+ return client.visibilityState === "visible";
188
+ })
189
+ .find((client) => {
190
+ // Find the client ID that's recorded in the
191
+ // set of clients that have registered the worker.
192
+ return activeClientIds.has(client.id);
193
+ });
194
+ }
195
+
196
+ /**
197
+ * @param {FetchEvent} event
198
+ * @param {Client | undefined} client
199
+ * @param {string} requestId
200
+ * @returns {Promise<Response>}
201
+ */
202
+ async function getResponse(event, client, requestId, requestInterceptedAt) {
203
+ // Clone the request because it might've been already used
204
+ // (i.e. its body has been read and sent to the client).
205
+ const requestClone = event.request.clone();
206
+
207
+ function passthrough() {
208
+ // Cast the request headers to a new Headers instance
209
+ // so the headers can be manipulated with.
210
+ const headers = new Headers(requestClone.headers);
211
+
212
+ // Remove the "accept" header value that marked this request as passthrough.
213
+ // This prevents request alteration and also keeps it compliant with the
214
+ // user-defined CORS policies.
215
+ const acceptHeader = headers.get("accept");
216
+ if (acceptHeader) {
217
+ const values = acceptHeader.split(",").map((value) => value.trim());
218
+ const filteredValues = values.filter((value) => value !== "msw/passthrough");
219
+
220
+ if (filteredValues.length > 0) {
221
+ headers.set("accept", filteredValues.join(", "));
222
+ } else {
223
+ headers.delete("accept");
224
+ }
225
+ }
226
+
227
+ return fetch(requestClone, { headers });
228
+ }
229
+
230
+ // Bypass mocking when the client is not active.
231
+ if (!client) {
232
+ return passthrough();
233
+ }
234
+
235
+ // Bypass initial page load requests (i.e. static assets).
236
+ // The absence of the immediate/parent client in the map of the active clients
237
+ // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
238
+ // and is not ready to handle requests.
239
+ if (!activeClientIds.has(client.id)) {
240
+ return passthrough();
241
+ }
242
+
243
+ // Notify the client that a request has been intercepted.
244
+ const serializedRequest = await serializeRequest(event.request);
245
+ const clientMessage = await sendToClient(
246
+ client,
247
+ {
248
+ type: "REQUEST",
249
+ payload: {
250
+ id: requestId,
251
+ interceptedAt: requestInterceptedAt,
252
+ ...serializedRequest,
253
+ },
254
+ },
255
+ [serializedRequest.body],
256
+ );
257
+
258
+ switch (clientMessage.type) {
259
+ case "MOCK_RESPONSE": {
260
+ return respondWithMock(clientMessage.data);
261
+ }
262
+
263
+ case "PASSTHROUGH": {
264
+ return passthrough();
265
+ }
266
+ }
267
+
268
+ return passthrough();
269
+ }
270
+
271
+ /**
272
+ * @param {Client} client
273
+ * @param {any} message
274
+ * @param {Array<Transferable>} transferrables
275
+ * @returns {Promise<any>}
276
+ */
277
+ function sendToClient(client, message, transferrables = []) {
278
+ return new Promise((resolve, reject) => {
279
+ const channel = new MessageChannel();
280
+
281
+ channel.port1.onmessage = (event) => {
282
+ if (event.data && event.data.error) {
283
+ return reject(event.data.error);
284
+ }
285
+
286
+ resolve(event.data);
287
+ };
288
+
289
+ client.postMessage(message, [channel.port2, ...transferrables.filter(Boolean)]);
290
+ });
291
+ }
292
+
293
+ /**
294
+ * @param {Response} response
295
+ * @returns {Response}
296
+ */
297
+ function respondWithMock(response) {
298
+ // Setting response status code to 0 is a no-op.
299
+ // However, when responding with a "Response.error()", the produced Response
300
+ // instance will have status code set to 0. Since it's not possible to create
301
+ // a Response instance with status code 0, handle that use-case separately.
302
+ if (response.status === 0) {
303
+ return Response.error();
304
+ }
305
+
306
+ const mockedResponse = new Response(response.body, response);
307
+
308
+ Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
309
+ value: true,
310
+ enumerable: true,
311
+ });
312
+
313
+ return mockedResponse;
314
+ }
315
+
316
+ /**
317
+ * @param {Request} request
318
+ */
319
+ async function serializeRequest(request) {
320
+ return {
321
+ url: request.url,
322
+ mode: request.mode,
323
+ method: request.method,
324
+ headers: Object.fromEntries(request.headers.entries()),
325
+ cache: request.cache,
326
+ credentials: request.credentials,
327
+ destination: request.destination,
328
+ integrity: request.integrity,
329
+ redirect: request.redirect,
330
+ referrer: request.referrer,
331
+ referrerPolicy: request.referrerPolicy,
332
+ body: await request.arrayBuffer(),
333
+ keepalive: request.keepalive,
334
+ };
335
+ }
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>polymo Connector Builder</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <link rel="icon" href="/static/favicon.ico" sizes="any" />
8
+ <link rel="icon" type="image/png" sizes="32x32" href="/static/logo.png" />
9
+ <link rel="icon" type="image/png" sizes="192x192" href="/static/logo192.png" />
10
+ <link rel="apple-touch-icon" href="/static/logo192.png" />
11
+ <link rel="stylesheet" href="/static/main.css" />
12
+ <script>
13
+ window.__polymo_INITIAL_CONFIG__ = {{ sample_config_dict | tojson }};
14
+ window.__polymo_SAMPLE_YAML__ = {{ sample_config | tojson }};
15
+ </script>
16
+ <script type="module" src="/static/main.js"></script>
17
+ </head>
18
+ <body>
19
+ <div id="root"></div>
20
+ </body>
21
+ </html>
polymo/cli.py ADDED
@@ -0,0 +1,73 @@
1
+ """Command line entry points for polymo."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from typing import Sequence
7
+
8
+
9
+ def main(argv: Sequence[str] | None = None) -> int:
10
+ parser = argparse.ArgumentParser(prog="polymo", description="Utilities for the polymo toolkit")
11
+ subparsers = parser.add_subparsers(dest="command")
12
+ bld_parser = subparsers.add_parser("builder", help="Launch the local builder UI")
13
+ bld_parser.add_argument("--host", default="127.0.0.1", help="Host to bind (default: %(default)s)")
14
+ bld_parser.add_argument("--port", type=int, default=8000, help="Port to bind (default: %(default)s)")
15
+ bld_parser.add_argument(
16
+ "--reload",
17
+ action="store_true",
18
+ help="Enable auto-reload (useful during development)",
19
+ )
20
+
21
+ smoke_parser = subparsers.add_parser("smoke", help="Launch the smoke test")
22
+ smoke_parser.add_argument(
23
+ "--config",
24
+ default="examples/jsonplaceholder.yml",
25
+ help="Path to the YAML config file (default: %(default)s)",
26
+ )
27
+ smoke_parser.add_argument(
28
+ "--stream",
29
+ default=None,
30
+ help="Optional stream name to load (default: first stream in config)",
31
+ )
32
+ smoke_parser.add_argument(
33
+ "--format",
34
+ default="polymo",
35
+ help="Registered DataSource name to use (default: %(default)s)",
36
+ )
37
+ smoke_parser.add_argument(
38
+ "--limit",
39
+ type=int,
40
+ default=5,
41
+ help="Number of rows to show from the resulting DataFrame",
42
+ )
43
+
44
+ args = parser.parse_args(argv)
45
+
46
+ # Check Spark Version
47
+ import pyspark
48
+ from packaging import version
49
+
50
+ if version.parse(pyspark.__version__) < version.parse("4.0.0"):
51
+ raise ImportError("pyspark>=4.0.0 is required: run pip install polymo[builder]")
52
+
53
+ if args.command == "builder":
54
+ import uvicorn
55
+ uvicorn.run(
56
+ "polymo.builder.app:create_app",
57
+ host=args.host,
58
+ port=args.port,
59
+ reload=args.reload,
60
+ factory=True,
61
+ )
62
+ return 0
63
+ elif args.command == "smoke":
64
+ from polymo.scripts.smoke import main as smoke_main
65
+ smoke_main(args)
66
+ return 0
67
+
68
+ parser.print_help()
69
+ return 1
70
+
71
+
72
+ if __name__ == "__main__": # pragma: no cover
73
+ raise SystemExit(main())