parsagon 0.10.17__tar.gz → 0.10.20__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.
- {parsagon-0.10.17 → parsagon-0.10.20}/PKG-INFO +6 -3
- {parsagon-0.10.17 → parsagon-0.10.20}/README.md +3 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/pyproject.toml +3 -3
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/executor.py +17 -7
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/highlights.js +56 -29
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/main.py +7 -2
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon.egg-info/PKG-INFO +6 -3
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon.egg-info/requires.txt +2 -2
- {parsagon-0.10.17 → parsagon-0.10.20}/setup.cfg +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/__init__.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/__init__.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/api.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/custom_function.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/exceptions.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/settings.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/tests/__init__.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/tests/api_mocks.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/tests/cli_mocks.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/tests/conftest.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/tests/test_executor.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/tests/test_invalid_args.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon/tests/test_pipeline_operations.py +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon.egg-info/SOURCES.txt +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon.egg-info/dependency_links.txt +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon.egg-info/entry_points.txt +0 -0
- {parsagon-0.10.17 → parsagon-0.10.20}/src/parsagon.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: parsagon
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.20
|
4
4
|
Summary: Allows you to create browser automations with natural language
|
5
5
|
Author-email: Sandy Suh <sandy@parsagon.io>
|
6
6
|
Project-URL: Homepage, https://parsagon.io
|
@@ -17,8 +17,8 @@ Requires-Dist: tqdm==4.66.1
|
|
17
17
|
Requires-Dist: PyVirtualDisplay==3.0
|
18
18
|
Requires-Dist: selenium-wire==5.1.0
|
19
19
|
Requires-Dist: cssselect==1.1.0
|
20
|
-
Requires-Dist: undetected-chromedriver==3.5.
|
21
|
-
Requires-Dist: webdriver-manager==4.0.
|
20
|
+
Requires-Dist: undetected-chromedriver==3.5.3
|
21
|
+
Requires-Dist: webdriver-manager==4.0.1
|
22
22
|
Requires-Dist: jsonpath-ng==1.5.3
|
23
23
|
Requires-Dist: simplejson==3.19.1
|
24
24
|
Provides-Extra: dev
|
@@ -75,6 +75,9 @@ parsagon.create('Go to https://www.google.com/. Type "the meaning of life" into
|
|
75
75
|
# Run a program
|
76
76
|
parsagon.run("My program")
|
77
77
|
|
78
|
+
# Run a program multiple times
|
79
|
+
parsagon.batch_runs("My batch name", "My program", runs=[{"variable_name": "value1"}, {"variable_name": "value2"}, ...])
|
80
|
+
|
78
81
|
# List your programs
|
79
82
|
parsagon.detail()
|
80
83
|
|
@@ -48,6 +48,9 @@ parsagon.create('Go to https://www.google.com/. Type "the meaning of life" into
|
|
48
48
|
# Run a program
|
49
49
|
parsagon.run("My program")
|
50
50
|
|
51
|
+
# Run a program multiple times
|
52
|
+
parsagon.batch_runs("My batch name", "My program", runs=[{"variable_name": "value1"}, {"variable_name": "value2"}, ...])
|
53
|
+
|
51
54
|
# List your programs
|
52
55
|
parsagon.detail()
|
53
56
|
|
@@ -16,7 +16,7 @@ line-length = 120
|
|
16
16
|
|
17
17
|
[project]
|
18
18
|
name = "parsagon"
|
19
|
-
version = "0.10.
|
19
|
+
version = "0.10.20"
|
20
20
|
description = "Allows you to create browser automations with natural language"
|
21
21
|
readme = "README.md"
|
22
22
|
requires-python = ">=3.8"
|
@@ -40,8 +40,8 @@ dependencies = [
|
|
40
40
|
'PyVirtualDisplay==3.0',
|
41
41
|
'selenium-wire==5.1.0',
|
42
42
|
'cssselect==1.1.0',
|
43
|
-
'undetected-chromedriver==3.5.
|
44
|
-
'webdriver-manager==4.0.
|
43
|
+
'undetected-chromedriver==3.5.3',
|
44
|
+
'webdriver-manager==4.0.1',
|
45
45
|
'jsonpath-ng==1.5.3',
|
46
46
|
'simplejson==3.19.1',
|
47
47
|
]
|
@@ -10,6 +10,8 @@ from urllib.parse import urljoin
|
|
10
10
|
import lxml.html
|
11
11
|
from pyvirtualdisplay import Display
|
12
12
|
import undetected_chromedriver as uc
|
13
|
+
from selenium import webdriver
|
14
|
+
from selenium.webdriver.chrome.service import Service as ChromeService
|
13
15
|
from selenium.webdriver.chrome.options import Options
|
14
16
|
from selenium.webdriver.common.action_chains import ActionChains
|
15
17
|
from selenium.webdriver.common.by import By
|
@@ -60,14 +62,19 @@ class Executor:
|
|
60
62
|
Executes code produced by GPT with the proper context. Records custom_function usage along the way.
|
61
63
|
"""
|
62
64
|
|
63
|
-
def __init__(self, headless=False, infer=False):
|
65
|
+
def __init__(self, headless=False, infer=False, use_uc=False):
|
64
66
|
self.headless = headless
|
65
67
|
if self.headless:
|
66
68
|
self.display = Display(visible=False, size=(1280, 1050)).start()
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
driver_executable_path = ChromeDriverManager().install()
|
70
|
+
if use_uc:
|
71
|
+
chrome_options = uc.ChromeOptions()
|
72
|
+
chrome_options.add_argument("--start-maximized")
|
73
|
+
self.driver = uc.Chrome(driver_executable_path=driver_executable_path, options=chrome_options)
|
74
|
+
else:
|
75
|
+
chrome_options = webdriver.ChromeOptions()
|
76
|
+
chrome_options.add_argument("--start-maximized")
|
77
|
+
self.driver = webdriver.Chrome(service=ChromeService(driver_executable_path), options=chrome_options)
|
71
78
|
self.max_elem_ids = defaultdict(int)
|
72
79
|
self.execution_context = {
|
73
80
|
"custom_assert": self.custom_assert,
|
@@ -553,7 +560,10 @@ class Executor:
|
|
553
560
|
finally:
|
554
561
|
self.driver.quit()
|
555
562
|
for proc in psutil.process_iter():
|
556
|
-
|
557
|
-
proc.
|
563
|
+
try:
|
564
|
+
if proc.name() == "chromedriver":
|
565
|
+
proc.kill()
|
566
|
+
except psutil.NoSuchProcess:
|
567
|
+
continue
|
558
568
|
if self.headless:
|
559
569
|
self.display.stop()
|
@@ -128,14 +128,24 @@ function hasValidData(element, dataType) {
|
|
128
128
|
return true;
|
129
129
|
}
|
130
130
|
|
131
|
-
function makeVisible(
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
131
|
+
function makeVisible(elements) {
|
132
|
+
|
133
|
+
let elemsToDisplayBlock = [];
|
134
|
+
for (let element of elements) {
|
135
|
+
const rects = element.getClientRects();
|
136
|
+
if (rects.length) {
|
137
|
+
const rect = rects[0];
|
138
|
+
if (rect.width * rect.height === 0) {
|
139
|
+
elemsToDisplayBlock.push(element);
|
140
|
+
}
|
137
141
|
}
|
138
142
|
}
|
143
|
+
|
144
|
+
requestAnimationFrame(() => {
|
145
|
+
for (let elem of elemsToDisplayBlock) {
|
146
|
+
elem.style.display = 'block';
|
147
|
+
}
|
148
|
+
})
|
139
149
|
}
|
140
150
|
|
141
151
|
const DUMMY_FRAGMENT = document.createDocumentFragment()
|
@@ -241,28 +251,45 @@ function removeTargetStoredCSS(element) {
|
|
241
251
|
element.style.removeProperty('position');
|
242
252
|
element.classList.remove(TARGET_STORED_CLASSNAME);
|
243
253
|
if (element.classList.contains(AUTOCOMPLETE_CLASSNAME)) {
|
244
|
-
addAutocompleteCSS(element);
|
254
|
+
addAutocompleteCSS([element]);
|
245
255
|
} else if (element.classList.contains(MOUSE_VISITED_CLASSNAME)) {
|
246
256
|
addMouseVisitedCSS(element);
|
247
257
|
}
|
248
258
|
};
|
249
|
-
function addAutocompleteCSS(
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
.
|
260
|
-
|
261
|
-
element.style.setProperty('position', 'relative', 'important');
|
259
|
+
function addAutocompleteCSS(elements) {
|
260
|
+
let setOutlineElements = [];
|
261
|
+
let makeStaticElements = [];
|
262
|
+
for (const element of elements) {
|
263
|
+
if (!element.classList.contains(TARGET_STORED_CLASSNAME)) {
|
264
|
+
setOutlineElements.push(element);
|
265
|
+
if (
|
266
|
+
window.getComputedStyle(element)
|
267
|
+
.position === 'static'
|
268
|
+
) {
|
269
|
+
makeStaticElements.push(element);
|
270
|
+
}
|
262
271
|
}
|
263
272
|
}
|
264
|
-
|
273
|
+
|
274
|
+
// Perform batch writes
|
275
|
+
requestAnimationFrame(() => {
|
276
|
+
for (const element of setOutlineElements) {
|
277
|
+
element.style.setProperty(
|
278
|
+
'outline',
|
279
|
+
'3px solid rgb(255, 177, 255)',
|
280
|
+
'important'
|
281
|
+
);
|
282
|
+
element.style.setProperty('outline-offset', '-3px', 'important');
|
283
|
+
}
|
284
|
+
for (const element of makeStaticElements) {
|
285
|
+
element.style.setProperty('position', 'relative', 'important');
|
286
|
+
}
|
287
|
+
for (const element of elements) {
|
288
|
+
element.classList.add(AUTOCOMPLETE_CLASSNAME);
|
289
|
+
}
|
290
|
+
});
|
265
291
|
};
|
292
|
+
|
266
293
|
function removeAutocompleteCSS(element) {
|
267
294
|
element.style.removeProperty('outline');
|
268
295
|
element.style.removeProperty('outline-offset');
|
@@ -300,13 +327,13 @@ function addAutocompletes() {
|
|
300
327
|
document.querySelectorAll(
|
301
328
|
cssSelector
|
302
329
|
);
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
330
|
+
|
331
|
+
// Set to similarElems where elem.classList.contains(TARGET_STORED_CLASSNAME) === false
|
332
|
+
const elemsToMakeVisibleAndAddAutocompleteCSS = Array.from(similarElems).filter((elem) => {
|
333
|
+
return !elem.classList.contains(TARGET_STORED_CLASSNAME);
|
334
|
+
});
|
335
|
+
makeVisible(elemsToMakeVisibleAndAddAutocompleteCSS);
|
336
|
+
addAutocompleteCSS(elemsToMakeVisibleAndAddAutocompleteCSS);
|
310
337
|
}
|
311
338
|
};
|
312
339
|
|
@@ -496,7 +523,7 @@ function handleMouseMove(e) {
|
|
496
523
|
return;
|
497
524
|
}
|
498
525
|
|
499
|
-
makeVisible(srcElement);
|
526
|
+
makeVisible([srcElement]);
|
500
527
|
|
501
528
|
addMouseVisitedCSS(srcElement);
|
502
529
|
window.prevDOM = srcElement;
|
@@ -325,8 +325,11 @@ def run(program_name, variables={}, headless=False, remote=False, verbose=False)
|
|
325
325
|
if "display" in globals_locals:
|
326
326
|
globals_locals["display"].stop()
|
327
327
|
for proc in psutil.process_iter():
|
328
|
-
|
329
|
-
proc.
|
328
|
+
try:
|
329
|
+
if proc.name() == "chromedriver":
|
330
|
+
proc.kill()
|
331
|
+
except psutil.NoSuchProcess:
|
332
|
+
continue
|
330
333
|
logger.info("Done.")
|
331
334
|
return globals_locals["output"]
|
332
335
|
|
@@ -359,6 +362,8 @@ def batch_runs(batch_name, program_name, runs=[], headless=False, ignore_errors=
|
|
359
362
|
pbar.set_description(f"An error occurred: {e} - Waiting 60s before retrying (Attempt {j+2}/3)")
|
360
363
|
time.sleep(60)
|
361
364
|
pbar.set_description(default_desc)
|
365
|
+
error = None
|
366
|
+
error_variables = None
|
362
367
|
continue
|
363
368
|
else:
|
364
369
|
if ignore_errors:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: parsagon
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.20
|
4
4
|
Summary: Allows you to create browser automations with natural language
|
5
5
|
Author-email: Sandy Suh <sandy@parsagon.io>
|
6
6
|
Project-URL: Homepage, https://parsagon.io
|
@@ -17,8 +17,8 @@ Requires-Dist: tqdm==4.66.1
|
|
17
17
|
Requires-Dist: PyVirtualDisplay==3.0
|
18
18
|
Requires-Dist: selenium-wire==5.1.0
|
19
19
|
Requires-Dist: cssselect==1.1.0
|
20
|
-
Requires-Dist: undetected-chromedriver==3.5.
|
21
|
-
Requires-Dist: webdriver-manager==4.0.
|
20
|
+
Requires-Dist: undetected-chromedriver==3.5.3
|
21
|
+
Requires-Dist: webdriver-manager==4.0.1
|
22
22
|
Requires-Dist: jsonpath-ng==1.5.3
|
23
23
|
Requires-Dist: simplejson==3.19.1
|
24
24
|
Provides-Extra: dev
|
@@ -75,6 +75,9 @@ parsagon.create('Go to https://www.google.com/. Type "the meaning of life" into
|
|
75
75
|
# Run a program
|
76
76
|
parsagon.run("My program")
|
77
77
|
|
78
|
+
# Run a program multiple times
|
79
|
+
parsagon.batch_runs("My batch name", "My program", runs=[{"variable_name": "value1"}, {"variable_name": "value2"}, ...])
|
80
|
+
|
78
81
|
# List your programs
|
79
82
|
parsagon.detail()
|
80
83
|
|
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
|