py2ls 0.1.10.0__py3-none-any.whl → 0.1.10.1__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.
Files changed (45) hide show
  1. py2ls/.git/COMMIT_EDITMSG +1 -1
  2. py2ls/.git/FETCH_HEAD +1 -1
  3. py2ls/.git/index +0 -0
  4. py2ls/.git/logs/HEAD +1 -0
  5. py2ls/.git/logs/refs/heads/main +1 -0
  6. py2ls/.git/logs/refs/remotes/origin/HEAD +1 -0
  7. py2ls/.git/logs/refs/remotes/origin/main +1 -0
  8. py2ls/.git/objects/27/aa6074f652bc6f7078f8647489d9ee8e24f0e2 +0 -0
  9. py2ls/.git/objects/28/c2969d785c1b892c2a96b3f00eba63a59811b3 +0 -0
  10. py2ls/.git/objects/2a/fdf45791a26d42ccead35ace76a8f0b2a56561 +0 -0
  11. py2ls/.git/objects/34/b6f3a2ee84f39bed4eee57f2c0e0afb994feb1 +0 -0
  12. py2ls/.git/objects/35/1a5f491ab97eee9d1ee699478d75a8bb5d3dc2 +0 -0
  13. py2ls/.git/objects/39/b13be65125556784e44c7a1d9821703c7ab67e +0 -0
  14. py2ls/.git/objects/3b/507acc7f23391644cc0b824b1e79fd2677a362 +0 -0
  15. py2ls/.git/objects/3d/9d10d27724657a436c65a6254bfd213d4b3562 +0 -0
  16. py2ls/.git/objects/47/6cbd5a7c5e35cddef2f8a38bdc4896d403b095 +0 -0
  17. py2ls/.git/objects/78/063f4c863fc371ec0313303c0a81283b35d9b6 +0 -0
  18. py2ls/.git/objects/82/70b319ce4046854fbe7dc41054b6c2d112dab2 +0 -0
  19. py2ls/.git/objects/85/aee46f478e9afdb84d50a05242c53b04ed2e21 +0 -0
  20. py2ls/.git/objects/86/e288b46f8fe179907e4413f665aeb5053fddb1 +0 -0
  21. py2ls/.git/objects/94/f7dbe88e80c4205a901b71eb8f181974376bba +0 -0
  22. py2ls/.git/objects/9b/ec5ee2236ee2d5532c36bfd132e23c58fdb69c +0 -0
  23. py2ls/.git/objects/b3/4f7f271c6d6105e35a6556ffda71d03afe8c96 +0 -0
  24. py2ls/.git/objects/b3/69579064bde9de9a19d114fc33e4e48cc8c0e4 +0 -0
  25. py2ls/.git/objects/bf/b54d65922ce1dfda1aaa014913a54e7172d0bc +0 -0
  26. py2ls/.git/objects/c1/397c6ed72c4e20ef6b9ab83163e9a6baba5b45 +0 -0
  27. py2ls/.git/objects/cc/45df1d317a2eb63ff1ff3a5f3b4a9f98fd92b5 +0 -0
  28. py2ls/.git/objects/d6/39e8af592cd75a318d8affddd1bcc70c2095f2 +0 -0
  29. py2ls/.git/objects/db/3f2cd643292057936230b95cf7ec3046affe11 +0 -0
  30. py2ls/.git/objects/de/214c626ac2dd2685bfaa0bc0fc20f528d014d7 +0 -0
  31. py2ls/.git/objects/e4/6c715352db9fe3c887a635f1916df4ca1f4ff9 +0 -0
  32. py2ls/.git/objects/e5/0580a0bd1e1b3d29f834382b80fceb61d5cf0c +0 -0
  33. py2ls/.git/objects/ec/d980279432b13f0374b90ca439a6329cdece0f +0 -0
  34. py2ls/.git/objects/ee/cee64eacaff022dcdc509c0c2b1da492f21060 +0 -0
  35. py2ls/.git/objects/f5/61c3c1bf1c9ea9c9d1f556a7be2869f71f3bdf +0 -0
  36. py2ls/.git/refs/heads/main +1 -1
  37. py2ls/.git/refs/remotes/origin/main +1 -1
  38. py2ls/batman.py +62 -47
  39. py2ls/ips.py +1 -1
  40. py2ls/netfinder.py +100 -1
  41. py2ls/ocr.py +557 -0
  42. py2ls/plot.py +24 -0
  43. {py2ls-0.1.10.0.dist-info → py2ls-0.1.10.1.dist-info}/METADATA +1 -1
  44. {py2ls-0.1.10.0.dist-info → py2ls-0.1.10.1.dist-info}/RECORD +45 -16
  45. {py2ls-0.1.10.0.dist-info → py2ls-0.1.10.1.dist-info}/WHEEL +0 -0
py2ls/.git/COMMIT_EDITMSG CHANGED
@@ -1 +1 @@
1
- update
1
+ reg_update
py2ls/.git/FETCH_HEAD CHANGED
@@ -1 +1 @@
1
- 6dc2cdf4a84e538e5d4777486aeff87e42f41799 branch 'main' of https://github.com/Jianfengliu0413/py2ls
1
+ 86e288b46f8fe179907e4413f665aeb5053fddb1 branch 'main' of https://github.com/Jianfengliu0413/py2ls
py2ls/.git/index CHANGED
Binary file
py2ls/.git/logs/HEAD CHANGED
@@ -19,3 +19,4 @@ d84688b54c0040a30976b3a6540bc47adf7ce680 32fd627b62fad7cf3b2f9e34ab9777126a0987a
19
19
  1c3f92adda34344bcbbbf9d409c79855ae2aaea8 bf67907e337021ebff434e02b19b30a741c144af Jianfeng <Jianfeng.Liu0413@gmail.com> 1720948339 +0200 commit: update
20
20
  bf67907e337021ebff434e02b19b30a741c144af a15389729850729fc7bd78a54f26fce77f30be12 Jianfeng <Jianfeng.Liu0413@gmail.com> 1721137352 +0200 commit: update ips
21
21
  a15389729850729fc7bd78a54f26fce77f30be12 6dc2cdf4a84e538e5d4777486aeff87e42f41799 Jianfeng <Jianfeng.Liu0413@gmail.com> 1723527985 +0200 commit: update
22
+ 6dc2cdf4a84e538e5d4777486aeff87e42f41799 86e288b46f8fe179907e4413f665aeb5053fddb1 Jianfeng <Jianfeng.Liu0413@gmail.com> 1725537214 +0200 commit: reg_update
@@ -19,3 +19,4 @@ d84688b54c0040a30976b3a6540bc47adf7ce680 32fd627b62fad7cf3b2f9e34ab9777126a0987a
19
19
  1c3f92adda34344bcbbbf9d409c79855ae2aaea8 bf67907e337021ebff434e02b19b30a741c144af Jianfeng <Jianfeng.Liu0413@gmail.com> 1720948339 +0200 commit: update
20
20
  bf67907e337021ebff434e02b19b30a741c144af a15389729850729fc7bd78a54f26fce77f30be12 Jianfeng <Jianfeng.Liu0413@gmail.com> 1721137352 +0200 commit: update ips
21
21
  a15389729850729fc7bd78a54f26fce77f30be12 6dc2cdf4a84e538e5d4777486aeff87e42f41799 Jianfeng <Jianfeng.Liu0413@gmail.com> 1723527985 +0200 commit: update
22
+ 6dc2cdf4a84e538e5d4777486aeff87e42f41799 86e288b46f8fe179907e4413f665aeb5053fddb1 Jianfeng <Jianfeng.Liu0413@gmail.com> 1725537214 +0200 commit: reg_update
@@ -137,3 +137,4 @@ a15389729850729fc7bd78a54f26fce77f30be12 a15389729850729fc7bd78a54f26fce77f30be1
137
137
  a15389729850729fc7bd78a54f26fce77f30be12 a15389729850729fc7bd78a54f26fce77f30be12 Jianfeng Liu <macjianfeng@JFLMBP.cin.medizin.uni-tuebingen.de> 1721326072 +0200 remote set-head
138
138
  a15389729850729fc7bd78a54f26fce77f30be12 a15389729850729fc7bd78a54f26fce77f30be12 Jianfeng Liu <macjianfeng@jflmbp.speedport.ip> 1723527981 +0200 remote set-head
139
139
  6dc2cdf4a84e538e5d4777486aeff87e42f41799 6dc2cdf4a84e538e5d4777486aeff87e42f41799 Jianfeng Liu <macjianfeng@jflmbp.speedport.ip> 1723527990 +0200 remote set-head
140
+ 86e288b46f8fe179907e4413f665aeb5053fddb1 86e288b46f8fe179907e4413f665aeb5053fddb1 Jianfeng Liu <macjianfeng@JFLMBP.cin.medizin.uni-tuebingen.de> 1725537218 +0200 remote set-head
@@ -18,3 +18,4 @@ d84688b54c0040a30976b3a6540bc47adf7ce680 32fd627b62fad7cf3b2f9e34ab9777126a0987a
18
18
  1c3f92adda34344bcbbbf9d409c79855ae2aaea8 bf67907e337021ebff434e02b19b30a741c144af Jianfeng <Jianfeng.Liu0413@gmail.com> 1720948342 +0200 update by push
19
19
  bf67907e337021ebff434e02b19b30a741c144af a15389729850729fc7bd78a54f26fce77f30be12 Jianfeng <Jianfeng.Liu0413@gmail.com> 1721137354 +0200 update by push
20
20
  a15389729850729fc7bd78a54f26fce77f30be12 6dc2cdf4a84e538e5d4777486aeff87e42f41799 Jianfeng <Jianfeng.Liu0413@gmail.com> 1723527989 +0200 update by push
21
+ 6dc2cdf4a84e538e5d4777486aeff87e42f41799 86e288b46f8fe179907e4413f665aeb5053fddb1 Jianfeng <Jianfeng.Liu0413@gmail.com> 1725537218 +0200 update by push
@@ -1 +1 @@
1
- 6dc2cdf4a84e538e5d4777486aeff87e42f41799
1
+ 86e288b46f8fe179907e4413f665aeb5053fddb1
@@ -1 +1 @@
1
- 6dc2cdf4a84e538e5d4777486aeff87e42f41799
1
+ 86e288b46f8fe179907e4413f665aeb5053fddb1
py2ls/batman.py CHANGED
@@ -7,20 +7,19 @@ import schedule
7
7
  import time
8
8
  from datetime import datetime
9
9
  import os
10
+ from pprint import pp
11
+ import json
10
12
 
11
13
 
12
- signature_styles = [
13
- "", # none
14
- (
15
- "------------------------------------------------------------------------------------------------\n"
16
- "Jianfeng Liu, PhD\n"
17
- "Institute of Medical Psychology and Behavioural Neurobiology\n"
18
- "University of Tübingen\n"
19
- "Otfried-Müller-Str. 25\n"
20
- "72076 Tübingen\n\n"
21
- ),
22
- f"Pappelweg 8\n72076 Tübingen\n\n",
23
- ]
14
+ def extract_kv(
15
+ dir_data="/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json",
16
+ idx=0,
17
+ ): # select the index
18
+ with open(dir_data, "r") as file:
19
+ email_login = json.load(file)
20
+ for i, (user, pd) in enumerate(email_login.items()):
21
+ if i == idx:
22
+ return user, pd
24
23
 
25
24
 
26
25
  def email_to(**kwargs):
@@ -40,7 +39,10 @@ def email_to(**kwargs):
40
39
  who, what, subject = None, None, None
41
40
  attachments = False
42
41
  pause_sec = False # default 10 seconds pause
43
- signature = signature_styles[0]
42
+
43
+ signature_styles = extract_kv(idx=1)[1] # signature list
44
+ signature = signature_styles[0] # default signature,None
45
+ verbose = True
44
46
 
45
47
  # params config
46
48
  for k, v in kwargs.items():
@@ -67,15 +69,14 @@ def email_to(**kwargs):
67
69
  if any(["sig" in k.lower()]): # 'attachments', 'file'
68
70
  signature = v # optional
69
71
  if not isinstance(v, str):
72
+ print(signature_styles)
70
73
  signature = signature_styles[v]
71
-
72
- # config sender
73
- email_address = "andyandhope@gmail.com"
74
- email_password = "myff ltls sfym wehe" # this is fake info
74
+ if any(["verb" in k.lower(), "show" in k.lower()]): # 'verbose', 'show'
75
+ verbose = v # verbose
75
76
 
76
77
  # Create email message
77
78
  message = MIMEMultipart()
78
- message["From"] = email_address
79
+ message["From"] = extract_kv()[0]
79
80
  message["To"] = who
80
81
  message["Subject"] = subject
81
82
 
@@ -97,10 +98,9 @@ def email_to(**kwargs):
97
98
  part.set_payload(attachment.read())
98
99
  encoders.encode_base64(part)
99
100
 
100
- fname = os.path.basename(file)
101
- print(fname)
102
101
  part.add_header(
103
- "Content-Disposition", f'attachment; filename= "{fname}"'
102
+ "Content-Disposition",
103
+ f'attachment; filename= "{os.path.basename(file)}"',
104
104
  )
105
105
  message.attach(part)
106
106
  except FileNotFoundError:
@@ -109,13 +109,21 @@ def email_to(**kwargs):
109
109
  print(f"Error attaching file {file}: {e}")
110
110
  if pause_sec:
111
111
  time.sleep(pause_sec)
112
+
112
113
  # Send the email
113
114
  try:
114
115
  with smtplib.SMTP("smtp.gmail.com", 587) as server:
115
116
  server.starttls()
116
- server.login(email_address, email_password)
117
- server.sendmail(email_address, who, message.as_string())
118
- print(f"Email successfully sent to {who}")
117
+ server.login(extract_kv()[0], extract_kv()[1])
118
+ server.sendmail(extract_kv()[0], who, message.as_string())
119
+ if verbose:
120
+ if attachments:
121
+ print(
122
+ f'\nEmail successfully sent to:\nto:"{who}"\nsubject:{subject}\nattached:'
123
+ )
124
+ pp(attachments)
125
+ else:
126
+ print(f'\nEmail successfully sent to:\nto:"{who}"\nsubject:{subject}')
119
127
  except Exception as e:
120
128
  print(f"Failed to send email: {e}")
121
129
 
@@ -158,26 +166,33 @@ def example_job():
158
166
 
159
167
 
160
168
  if __name__ == "__main__":
161
- # Schedule the job to run every minute
162
- schedule.every(1).minutes.do(example_job)
163
-
164
- while True:
165
- schedule.run_pending()
166
- time.sleep(1)
167
-
168
- # example2:
169
- email_to(
170
- who="Jianfeng.Liu@medizin.uni-tuebingen.de",
171
- subj="test",
172
- wha="this is the body",
173
- signat="\n\n Best, Jianfeng\n",
174
- att="/Users/macjianfeng/Dropbox/Downloads/_*_doc-xlsx/20240822-preprints_full_20190101_20201031-2.xlsx",
175
- )
176
- # example3:
177
- email_to(
178
- who="Jianfeng.Liu@medizin.uni-tuebingen.de",
179
- subj="test",
180
- wha="this is the test",
181
- signat=2,
182
- att="/Users/macjianfeng/Dropbox/Downloads/_*_doc-xlsx/20240822-preprints_full_20190101_20201031-2.xlsx",
183
- )
169
+ pass
170
+ # # Schedule the job to run every minute
171
+ # schedule.every(1).minutes.do(example_job)
172
+
173
+ # while True:
174
+ # schedule.run_pending()
175
+ # time.sleep(1)
176
+
177
+ # # example2:
178
+ # email_to(
179
+ # who="Jianfeng.Liu@medizin.uni-tuebingen.de",
180
+ # subj="test",
181
+ # wha="this is the body",
182
+ # signat="\n\n Best, Jianfeng\n",
183
+ # att="/Users/macjianfeng/Dropbox/Downloads/_*_doc-xlsx/20240822-preprints_full_20190101_20201031-2.xlsx",
184
+ # )
185
+
186
+ # # example3:
187
+ # email_to(
188
+ # who="Jianfeng.Liu@medizin.uni-tuebingen.de",
189
+ # subj="test",
190
+ # wha="this is the test",
191
+ # signat=2,
192
+ # # att="/Users/macjianfeng/Dropbox/Downloads/_*_doc-xlsx/20240822-preprints_full_20190101_20201031-2.xlsx",
193
+ # )
194
+
195
+ # # example_4
196
+ # print(extract_kv(idx=0))
197
+ # print(extract_kv(idx=0)[0])
198
+ # print(extract_kv(idx=1))
py2ls/ips.py CHANGED
@@ -295,7 +295,7 @@ def is_text(s):
295
295
  return has_alpha and has_non_alpha
296
296
 
297
297
 
298
- def strcmp(search_term, candidates, ignore_case=True, verbose=True, scorer="WR"):
298
+ def strcmp(search_term, candidates, ignore_case=True, verbose=False, scorer="WR"):
299
299
  """
300
300
  Compares a search term with a list of candidate strings and finds the best match based on similarity score.
301
301
 
py2ls/netfinder.py CHANGED
@@ -63,6 +63,36 @@ def user_agent(
63
63
  return output_ua
64
64
 
65
65
 
66
+ def get_tags(content, ascending=True):
67
+ tag_names = set()
68
+
69
+ # Iterate through all tags in the parsed HTML
70
+ for tag in content.find_all(True): # `True` finds all tags
71
+ tag_names.add(tag.name) # Add the tag name to the set
72
+
73
+ # Convert set to a sorted list for easier reading (optional)
74
+ if ascending is None:
75
+ return tag_names
76
+ else:
77
+ if ascending:
78
+ return sorted(tag_names)
79
+ else:
80
+ return tag_names
81
+
82
+
83
+ def get_attr(content, where, attr):
84
+ all_tags = get_tags(content)
85
+ if all([where, attr]):
86
+ if where in all_tags:
87
+ element_ = content.find_all(where)
88
+ return [i[attr] for i in element_]
89
+ else:
90
+ print(
91
+ f"cannot find attr {attr} in tag_name{where}\n or possibly cannot find the tag_names:"
92
+ )
93
+ pp(all_tags)
94
+
95
+
66
96
  def extract_text_from_content(
67
97
  content, content_type="text/html", where=None, what=None, extend=True, **kwargs
68
98
  ):
@@ -128,7 +158,9 @@ def extract_text_from_content(
128
158
  result_set = content.find_all(where, **search_kwargs)
129
159
  else:
130
160
  result_set = content.find_all(where, attrs=dict(**search_kwargs))
131
-
161
+ if "get" in kwargs:
162
+ attr = kwargs["get"]
163
+ return get_attr(content, where, attr)
132
164
  if not result_set:
133
165
  print("Failed: check the 'attrs' setting: attrs={'id':'xample'}")
134
166
  if extend:
@@ -216,6 +248,60 @@ def get_cookies(url, login={"username": "your_username", "password": "your_passw
216
248
  return cookies_dict
217
249
 
218
250
 
251
+ ### 更加平滑地移动鼠标, 这样更容易反爬
252
+ def scroll_smth_steps(driver, scroll_pause=0.5, min_step=200, max_step=600):
253
+ """Smoothly scrolls down the page to trigger lazy loading."""
254
+ current_scroll_position = 0
255
+ end_of_page = driver.execute_script("return document.body.scrollHeight")
256
+
257
+ while current_scroll_position < end_of_page:
258
+ step = random.randint(min_step, max_step)
259
+ driver.execute_script(f"window.scrollBy(0, {step});")
260
+ time.sleep(scroll_pause)
261
+
262
+ # Update the current scroll position
263
+ current_scroll_position += step
264
+ end_of_page = driver.execute_script("return document.body.scrollHeight")
265
+
266
+
267
+ def scroll_inf2end(driver, scroll_pause=1):
268
+ """Continuously scrolls until the end of the page is reached."""
269
+ last_height = driver.execute_script("return document.body.scrollHeight")
270
+
271
+ while True:
272
+ # Scroll to the bottom
273
+ driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
274
+ time.sleep(scroll_pause)
275
+
276
+ # Get the new height after scrolling
277
+ new_height = driver.execute_script("return document.body.scrollHeight")
278
+ if new_height == last_height:
279
+ break # Exit if no new content is loaded
280
+ last_height = new_height
281
+
282
+
283
+ def corr_by_kind(wait_until_kind):
284
+ """
285
+ Map the 'wait_until_kind' string to the appropriate Selenium By strategy.
286
+ """
287
+ if "tag" in wait_until_kind:
288
+ return By.TAG_NAME
289
+ elif "css" in wait_until_kind:
290
+ return By.CSS_SELECTOR
291
+ elif "id" in wait_until_kind:
292
+ return By.ID
293
+ elif "name" in wait_until_kind:
294
+ return By.NAME
295
+ elif "class" in wait_until_kind:
296
+ return By.CLASS_NAME
297
+ elif "path" in wait_until_kind:
298
+ return By.XPATH
299
+ elif "link" in wait_until_kind or "text" in wait_until_kind:
300
+ return By.LINK_TEXT
301
+ else:
302
+ raise ValueError(f"Unsupported wait_until_kind: {wait_until_kind}")
303
+
304
+
219
305
  def fetch_all(
220
306
  url,
221
307
  parser="lxml",
@@ -224,6 +310,8 @@ def fetch_all(
224
310
  timeout=10,
225
311
  retry=2,
226
312
  wait=0,
313
+ wait_until=None,
314
+ wait_until_kind=None,
227
315
  scroll_try=3,
228
316
  login_url=None,
229
317
  username=None,
@@ -308,7 +396,10 @@ def fetch_all(
308
396
  prefs = {"profile.managed_default_content_settings.images": 2}
309
397
  chrome_options.add_experimental_option("prefs", prefs)
310
398
  # chrome_options.page_load_strategy = capability
399
+
311
400
  service = Service(ChromeDriverManager().install())
401
+ # driver_path='/Users/macjianfeng/.wdm/drivers/chromedriver/mac64/127.0.6533.119/chromedriver-mac-arm64/chromedriver'
402
+ # service=Service(executable_path=driver_path)
312
403
 
313
404
  driver_ = webdriver.Chrome(service=service, options=chrome_options)
314
405
 
@@ -323,6 +414,11 @@ def fetch_all(
323
414
  wait_ = 0
324
415
  driver_.implicitly_wait(wait_)
325
416
 
417
+ if wait_until is not None and wait_until_kind is not None:
418
+ strategy = corr_by_kind(wait_until_kind)
419
+ WebDriverWait(driver_, timeout).until(
420
+ EC.presence_of_element_located((strategy, wait_until))
421
+ )
326
422
  if login_url and login_dict:
327
423
  cookies = get_cookies(url=login_url, login=login_dict)
328
424
  driver_.get(url)
@@ -358,6 +454,9 @@ def fetch_all(
358
454
  # EC.presence_of_element_located((by, where))
359
455
  # )
360
456
 
457
+ # # scroll down the page by a certain number of pixels
458
+ scroll_smth_steps(driver_)
459
+
361
460
  # 设置轮询
362
461
  for attempt in range(scroll_try):
363
462
  page_source = driver_.page_source
py2ls/ocr.py ADDED
@@ -0,0 +1,557 @@
1
+ import easyocr
2
+ import cv2
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ from py2ls.ips import (
6
+ strcmp,
7
+ detect_angle,
8
+ ) # Ensure this function is defined in your 'ips' module
9
+ from spellchecker import SpellChecker
10
+ import re
11
+
12
+ from PIL import Image, ImageDraw, ImageFont
13
+ import PIL.PngImagePlugin
14
+
15
+ """
16
+ Optical Character Recognition (OCR)
17
+ """
18
+
19
+ # Valid language codes
20
+ lang_valid = {
21
+ "english": "en",
22
+ "thai": "th",
23
+ "chinese_traditional": "ch_tra",
24
+ "chinese": "ch_sim",
25
+ "japanese": "ja",
26
+ "korean": "ko",
27
+ "tamil": "ta",
28
+ "telugu": "te",
29
+ "kannada": "kn",
30
+ "german": "de",
31
+ }
32
+
33
+
34
+ def lang_auto_detect(lang):
35
+ res_lang = []
36
+ if isinstance(lang, str):
37
+ lang = [lang]
38
+ for i in lang:
39
+ res_lang.append(lang_valid[strcmp(i, list(lang_valid.keys()))[0]])
40
+ return res_lang
41
+
42
+
43
+ def determine_src_points(image):
44
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
45
+ _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
46
+ contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
47
+
48
+ # Sort contours by area and pick the largest one
49
+ contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
50
+ src_points = None
51
+
52
+ for contour in contours:
53
+ epsilon = 0.02 * cv2.arcLength(contour, True)
54
+ approx = cv2.approxPolyDP(contour, epsilon, True)
55
+ if len(approx) == 4: # We need a quadrilateral
56
+ src_points = np.array(approx, dtype="float32")
57
+ break
58
+
59
+ if src_points is not None:
60
+ # Order points in a specific order (top-left, top-right, bottom-right, bottom-left)
61
+ src_points = src_points.reshape(4, 2)
62
+ rect = np.zeros((4, 2), dtype="float32")
63
+ s = src_points.sum(axis=1)
64
+ diff = np.diff(src_points, axis=1)
65
+ rect[0] = src_points[np.argmin(s)]
66
+ rect[2] = src_points[np.argmax(s)]
67
+ rect[1] = src_points[np.argmin(diff)]
68
+ rect[3] = src_points[np.argmax(diff)]
69
+ src_points = rect
70
+ else:
71
+ # If no rectangle is detected, fallback to a default or user-defined points
72
+ height, width = image.shape[:2]
73
+ src_points = np.array(
74
+ [[0, 0], [width - 1, 0], [width - 1, height - 1], [0, height - 1]],
75
+ dtype="float32",
76
+ )
77
+ return src_points
78
+
79
+
80
+ def get_default_camera_matrix(image_shape):
81
+ height, width = image_shape[:2]
82
+ focal_length = width
83
+ center = (width / 2, height / 2)
84
+ camera_matrix = np.array(
85
+ [[focal_length, 0, center[0]], [0, focal_length, center[1]], [0, 0, 1]],
86
+ dtype="float32",
87
+ )
88
+ dist_coeffs = np.zeros((4, 1)) # Assuming no distortion
89
+ return camera_matrix, dist_coeffs
90
+
91
+
92
+ def correct_perspective(image, src_points):
93
+ # Define the destination points for the perspective transform
94
+ width, height = 1000, 1000 # Adjust size as needed
95
+ dst_points = np.array(
96
+ [[0, 0], [width - 1, 0], [width - 1, height - 1], [0, height - 1]],
97
+ dtype="float32",
98
+ )
99
+
100
+ # Calculate the perspective transform matrix
101
+ M = cv2.getPerspectiveTransform(src_points, dst_points)
102
+ # Apply the perspective transform
103
+ corrected_image = cv2.warpPerspective(image, M, (width, height))
104
+ return corrected_image
105
+
106
+
107
+ def detect_text_orientation(image):
108
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
109
+ edges = cv2.Canny(gray, 50, 150, apertureSize=3)
110
+ lines = cv2.HoughLines(edges, 1, np.pi / 180, 200)
111
+
112
+ if lines is None:
113
+ return 0
114
+
115
+ angles = []
116
+ for rho, theta in lines[:, 0]:
117
+ angle = theta * 180 / np.pi
118
+ if angle > 90:
119
+ angle -= 180
120
+ angles.append(angle)
121
+
122
+ median_angle = np.median(angles)
123
+ return median_angle
124
+
125
+
126
+ def rotate_image(image, angle):
127
+ center = (image.shape[1] // 2, image.shape[0] // 2)
128
+ rot_mat = cv2.getRotationMatrix2D(center, angle, 1.0)
129
+ rotated_image = cv2.warpAffine(
130
+ image, rot_mat, (image.shape[1], image.shape[0]), flags=cv2.INTER_LINEAR
131
+ )
132
+ return rotated_image
133
+
134
+
135
+ def correct_skew(image):
136
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
137
+ coords = np.column_stack(np.where(gray > 0))
138
+ angle = cv2.minAreaRect(coords)[-1]
139
+ if angle < -45:
140
+ angle = -(90 + angle)
141
+ else:
142
+ angle = -angle
143
+ return rotate_image(image, angle)
144
+
145
+
146
+ def undistort_image(image, camera_matrix, dist_coeffs):
147
+ return cv2.undistort(image, camera_matrix, dist_coeffs)
148
+
149
+
150
+ def add_text_pil(image, text, position, font_size=10, color=(255, 0, 0)):
151
+ # Convert the image to PIL format
152
+ pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
153
+ # Create a drawing context
154
+ draw = ImageDraw.Draw(pil_image)
155
+ # Define the font (make sure to use a font that supports Chinese characters)
156
+ try:
157
+ font = ImageFont.truetype(
158
+ "/System/Library/Fonts/Supplemental/Songti.ttc", font_size
159
+ )
160
+ except IOError:
161
+ font = ImageFont.load_default()
162
+
163
+ # cal top_left position
164
+ # Measure text size using textbbox
165
+ text_bbox = draw.textbbox((0, 0), text, font=font)
166
+ text_width = text_bbox[2] - text_bbox[0]
167
+ text_height = text_bbox[3] - text_bbox[1]
168
+ # Calculate 5% of the text height for upward adjustment
169
+ offset = int(0.5 * text_height) # 上移动 50%
170
+ # Adjust position to match OpenCV's bottom-left alignment
171
+ adjusted_position = (position[0], position[1] - text_height - offset)
172
+
173
+ # Add text to the image
174
+ draw.text(adjusted_position, text, font=font, fill=color)
175
+ # Convert the image back to OpenCV format
176
+ image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
177
+ return image
178
+
179
+
180
+ def preprocess_img(
181
+ image,
182
+ grayscale=True,
183
+ threshold=True,
184
+ threshold_method="adaptive",
185
+ rotate="auto",
186
+ skew=True,
187
+ denoise=True,
188
+ blur_ksize=(5, 5),
189
+ morph=True,
190
+ morph_op="open",
191
+ morph_kernel_size=(3, 3),
192
+ enhance_contrast=True,
193
+ clahe_clip=2.0,
194
+ clahe_grid_size=(8, 8),
195
+ edge_detection=False,
196
+ ):
197
+ """
198
+ 预处理步骤:
199
+
200
+ 转换为灰度图像: 如果 grayscale 为 True,将图像转换为灰度图像。
201
+ 二值化处理: 根据 threshold 和 threshold_method 参数,对图像进行二值化处理。
202
+ 降噪处理: 使用高斯模糊对图像进行降噪。
203
+ 形态学处理: 根据 morph_op 参数选择不同的形态学操作(开运算、闭运算、膨胀、腐蚀),用于去除噪声或填补孔洞。
204
+ 对比度增强: 使用 CLAHE 技术增强图像对比度。
205
+ 边缘检测: 如果 edge_detection 为 True,使用 Canny 边缘检测算法。
206
+
207
+ 预处理图像以提高 OCR 识别准确性。
208
+ 参数:
209
+ image: 输入的图像路径或图像数据。
210
+ grayscale: 是否将图像转换为灰度图像。
211
+ threshold: 是否对图像进行二值化处理。
212
+ threshold_method: 二值化方法,可以是 'global' 或 'adaptive'。
213
+ denoise: 是否对图像进行降噪处理。
214
+ blur_ksize: 高斯模糊的核大小。
215
+ morph: 是否进行形态学处理。
216
+ morph_op: 形态学操作的类型,包括 'open'(开运算)、'close'(闭运算)、'dilate'(膨胀)、'erode'(腐蚀)。
217
+ morph_kernel_size: 形态学操作的内核大小。
218
+ enhance_contrast: 是否增强图像对比度。
219
+ clahe_clip: CLAHE(对比度受限的自适应直方图均衡)的剪裁限制。
220
+ clahe_grid_size: CLAHE 的网格大小。
221
+ edge_detection: 是否进行边缘检测。
222
+ """
223
+ if isinstance(image, PIL.PngImagePlugin.PngImageFile):
224
+ image = np.array(image)
225
+ if isinstance(image, str):
226
+ image = cv2.imread(image)
227
+ if not isinstance(image, np.ndarray):
228
+ image = np.array(image)
229
+ if image.shape[1] == 4: # Check if it has an alpha channel
230
+ # Drop the alpha channel (if needed), or handle it as required
231
+ image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
232
+ else:
233
+ # Convert RGB to BGR for OpenCV compatibility
234
+ image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
235
+
236
+ # Rotate image
237
+ if rotate == "auto":
238
+ angle = detect_angle(image, by="fft")
239
+ img_preprocessed = rotate_image(image, angle)
240
+ else:
241
+ img_preprocessed = image
242
+
243
+ # # Correct skew
244
+ # if skew:
245
+ # img_preprocessed = correct_skew(image)
246
+
247
+ # Convert to grayscale
248
+ if grayscale:
249
+ img_preprocessed = cv2.cvtColor(img_preprocessed, cv2.COLOR_BGR2GRAY)
250
+
251
+ # Thresholding
252
+ if threshold:
253
+ if threshold_method == "adaptive":
254
+ image = cv2.adaptiveThreshold(
255
+ img_preprocessed,
256
+ 255,
257
+ cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
258
+ cv2.THRESH_BINARY,
259
+ 11,
260
+ 2,
261
+ )
262
+ elif threshold_method == "global":
263
+ _, img_preprocessed = cv2.threshold(
264
+ img_preprocessed, 127, 255, cv2.THRESH_BINARY
265
+ )
266
+
267
+ # Denoise
268
+ if denoise:
269
+ img_preprocessed = cv2.GaussianBlur(img_preprocessed, blur_ksize, 0)
270
+
271
+ # 形态学处理
272
+ if morph:
273
+ kernel = cv2.getStructuringElement(cv2.MORPH_RECT, morph_kernel_size)
274
+ if morph_op == "close": # 闭运算
275
+ # 目的: 闭运算用于填补前景物体中的小孔或间隙,同时保留其形状和大小。
276
+ # 工作原理: 闭运算先进行膨胀,然后进行腐蚀。膨胀步骤填补小孔或间隙,腐蚀步骤恢复较大物体的形状。
277
+ # 效果:
278
+ # 填补前景物体中的小孔和间隙。
279
+ # 平滑较大物体的边缘。
280
+ # 示例用途: 填补物体中的小孔或间隙。
281
+ img_preprocessed = cv2.morphologyEx(
282
+ img_preprocessed, cv2.MORPH_CLOSE, kernel
283
+ )
284
+ elif morph_op == "open": # 开运算
285
+ # 目的: 开运算用于去除背景中的小物体或噪声,同时保留较大物体的形状和大小。
286
+ # 工作原理: 开运算先进行腐蚀,然后进行膨胀。腐蚀步骤去除小规模的噪声,膨胀步骤恢复剩余物体的大小。
287
+ # 效果:
288
+ # 去除前景中的小物体。
289
+ # 平滑较大物体的轮廓。
290
+ # 示例用途: 去除小噪声或伪影,同时保持较大物体完整。
291
+ img_preprocessed = cv2.morphologyEx(
292
+ img_preprocessed, cv2.MORPH_OPEN, kernel
293
+ )
294
+ elif morph_op == "dilate": # 膨胀
295
+ # 目的: 膨胀操作在物体边界上添加像素。它可以用来填补物体中的小孔或连接相邻的物体。
296
+ # 工作原理: 内核在图像上移动,每个位置上的像素值被设置为内核覆盖区域中的最大值。
297
+ # 效果:
298
+ # 物体变大。
299
+ # 填补物体中的小孔或间隙。
300
+ # 示例用途: 填补物体中的小孔或连接断裂的物体部分。
301
+ img_preprocessed = cv2.dilate(img_preprocessed, kernel)
302
+ elif morph_op == "erode": # 腐蚀
303
+ # 目的: 腐蚀操作用于去除物体边界上的像素。它可以用来去除小规模的噪声,并将靠近的物体分开。
304
+ # 工作原理: 内核(结构元素)在图像上移动,每个位置上的像素值被设置为内核覆盖区域中的最小值。
305
+ # 效果:
306
+ # 物体变小。
307
+ # 去除图像中的小白点(在白色前景/黑色背景的图像中)。
308
+ # 示例用途: 去除二值图像中的小噪声或分离相互接触的物体
309
+ img_preprocessed = cv2.erode(img_preprocessed, kernel)
310
+
311
+ # 对比度增强
312
+ if enhance_contrast:
313
+ clahe = cv2.createCLAHE(clipLimit=clahe_clip, tileGridSize=clahe_grid_size)
314
+ img_preprocessed = clahe.apply(img_preprocessed)
315
+
316
+ # 边缘检测
317
+ if edge_detection:
318
+ img_preprocessed = cv2.Canny(img_preprocessed, 100, 200)
319
+
320
+ return img_preprocessed
321
+
322
+
323
+ def text_postprocess(
324
+ text,
325
+ spell_check=True,
326
+ clean=True,
327
+ filter=dict(min_length=2),
328
+ pattern=None,
329
+ merge=True,
330
+ ):
331
+
332
+ def correct_spelling(text_list):
333
+ spell = SpellChecker()
334
+ corrected_text = [spell.candidates(word) for word in text_list]
335
+ return corrected_text
336
+
337
+ def clean_text(text_list):
338
+ cleaned_text = [re.sub(r"[^\w\s]", "", text) for text in text_list]
339
+ return cleaned_text
340
+
341
+ def filter_text(text_list, min_length=2):
342
+ filtered_text = [text for text in text_list if len(text) >= min_length]
343
+ return filtered_text
344
+
345
+ def extract_patterns(text_list, pattern):
346
+ pattern = re.compile(pattern)
347
+ matched_text = [text for text in text_list if pattern.search(text)]
348
+ return matched_text
349
+
350
+ def merge_fragments(text_list):
351
+ merged_text = " ".join(text_list)
352
+ return merged_text
353
+
354
+ results = text
355
+ print(results)
356
+ if spell_check:
357
+ results = correct_spelling(results)
358
+ if clean:
359
+ results = clean_text(results)
360
+ if filter:
361
+ results = filter_text(
362
+ results, min_length=postprocess["filter"].get("min_length", 2)
363
+ )
364
+ if pattern:
365
+ results = extract_patterns(results, postprocess["pattern"])
366
+ if merge:
367
+ results = merge_fragments(results)
368
+
369
+
370
+ # https://www.jaided.ai/easyocr/documentation/
371
+ # extract text from an image with EasyOCR
372
+ def get_text(
373
+ image,
374
+ lang=["ch_sim", "en"],
375
+ thr=0.25,
376
+ gpu=True,
377
+ decoder="wordbeamsearch", #'greedy', 'beamsearch' and 'wordbeamsearch'(hightly accurate)
378
+ output="all",
379
+ preprocess=None,
380
+ postprocess="not ready",
381
+ show=True,
382
+ ax=None,
383
+ cmap=cv2.COLOR_BGR2RGB, # draw_box
384
+ font=cv2.FONT_HERSHEY_SIMPLEX,
385
+ fontScale=0.8,
386
+ thickness_text=2, # Line thickness of 2 px
387
+ color_box=(0, 255, 0), # draw_box
388
+ color_text=(0, 0, 255), # draw_box
389
+ **kwargs,
390
+ ):
391
+ """
392
+ 功能: 该函数使用 EasyOCR 进行文本识别,并允许自定义图像预处理步骤和结果展示。
393
+ 参数:
394
+ image: 输入的图像路径或图像数据。
395
+ lang: OCR 语言列表。
396
+ thr: 置信度阈值,低于此阈值的检测结果将被过滤。
397
+ gpu: 是否使用 GPU。
398
+ output: 输出类型,可以是 'all'(返回所有检测结果)、'text'(返回文本)、'score'(返回置信度分数)、'box'(返回边界框)。
399
+ preprocess: 预处理参数字典,传递给 preprocess_img 函数。
400
+ show: 是否显示结果图像。
401
+ ax: 用于显示图像的 Matplotlib 子图。
402
+ cmap: 用于显示图像的颜色映射。
403
+ color_box: 边界框的颜色。
404
+ color_text: 文本的颜色。
405
+ kwargs: 传递给 EasyOCR readtext 函数的其他参数。
406
+
407
+ # Uage
408
+ image_path = 'car_plate.jpg' # 替换为你的图像路径
409
+ results = get_text(
410
+ image_path,
411
+ lang=["en"],
412
+ gpu=False,
413
+ output="text",
414
+ preprocess={
415
+ "grayscale": True,
416
+ "threshold": True,
417
+ "threshold_method": 'adaptive',
418
+ "denoise": True,
419
+ "blur_ksize": (5, 5),
420
+ "morph": True,
421
+ "morph_op": 'close',
422
+ "morph_kernel_size": (3, 3),
423
+ "enhance_contrast": True,
424
+ "clahe_clip": 2.0,
425
+ "clahe_grid_size": (8, 8),
426
+ "edge_detection": False
427
+ },
428
+ adjust_contrast=0.7
429
+ )
430
+ """
431
+ lang = lang_auto_detect(lang)
432
+ print(f"detecting language(s):{lang}")
433
+ if isinstance(image, str):
434
+ image = cv2.imread(image)
435
+
436
+ # Ensure lang is always a list
437
+ if isinstance(lang, str):
438
+ lang = [lang]
439
+
440
+ # ! preprocessing img
441
+ if preprocess is None:
442
+ preprocess = {}
443
+ image_process = preprocess_img(image, **preprocess)
444
+
445
+ # Perform OCR on the image
446
+ reader = easyocr.Reader(lang, gpu=gpu)
447
+ detections = reader.readtext(image_process, decoder=decoder, **kwargs)
448
+ if postprocess is None:
449
+ postprocess = dict(
450
+ spell_check=True,
451
+ clean=True,
452
+ filter=dict(min_length=2),
453
+ pattern=None,
454
+ merge=True,
455
+ )
456
+ text_corr = []
457
+ for _, text, _ in detections:
458
+ text_corr.extend(text_postprocess(text, **postprocess))
459
+ if show:
460
+ if ax is None:
461
+ ax = plt.gca()
462
+ for bbox, text, score in detections:
463
+ if score > thr:
464
+ top_left = tuple(map(int, bbox[0]))
465
+ bottom_right = tuple(map(int, bbox[2]))
466
+ image = cv2.rectangle(image, top_left, bottom_right, color_box, 2)
467
+ # image = cv2.putText(
468
+ # image, text, top_left, font, fontScale, color_text, thickness_text
469
+ # )
470
+ image = add_text_pil(
471
+ image, text, top_left, font_size=fontScale * 32, color=color_text
472
+ )
473
+
474
+ img_cmp = cv2.cvtColor(image, cmap)
475
+ ax.imshow(img_cmp)
476
+ ax.axis("off")
477
+ # plt.show()
478
+ # 根据输出类型返回相应的结果
479
+ if output == "all":
480
+ return ax, detections
481
+ elif "t" in output.lower() and "x" in output.lower():
482
+ # 提取文本,过滤低置信度的结果
483
+ text = [text_ for _, text_, score_ in detections if score_ >= thr]
484
+ if postprocess:
485
+ return ax, text
486
+ else:
487
+ return text_corr
488
+ elif "score" in output.lower() or "prob" in output.lower():
489
+ # 提取分数
490
+ scores = [score_ for _, _, score_ in detections]
491
+ return ax, scores
492
+ elif "box" in output.lower():
493
+ # 提取边界框,过滤低置信度的结果
494
+ bboxes = [bbox_ for bbox_, _, score_ in detections if score_ >= thr]
495
+ return ax, bboxes
496
+ else:
497
+ # 默认返回所有检测信息
498
+ return ax, detections
499
+ else:
500
+ # 根据输出类型返回相应的结果
501
+ if output == "all":
502
+ return detections
503
+ elif "t" in output.lower() and "x" in output.lower():
504
+ # 提取文本,过滤低置信度的结果
505
+ text = [text_ for _, text_, score_ in detections if score_ >= thr]
506
+ return text
507
+ elif "score" in output.lower() or "prob" in output.lower():
508
+ # 提取分数
509
+ scores = [score_ for _, _, score_ in detections]
510
+ return scores
511
+ elif "box" in output.lower():
512
+ # 提取边界框,过滤低置信度的结果
513
+ bboxes = [bbox_ for bbox_, _, score_ in detections if score_ >= thr]
514
+ return bboxes
515
+ else:
516
+ # 默认返回所有检测信息
517
+ return detections
518
+
519
+
520
+ def draw_box(
521
+ image,
522
+ detections=None,
523
+ thr=0.25,
524
+ cmap=cv2.COLOR_BGR2RGB,
525
+ color_box=(0, 255, 0), # draw_box
526
+ color_text=(0, 0, 255), # draw_box
527
+ font_scale=0.8,
528
+ show=True,
529
+ ax=None,
530
+ **kwargs,
531
+ ):
532
+
533
+ if ax is None:
534
+ ax = plt.gca()
535
+ if isinstance(image, str):
536
+ image = cv2.imread(image)
537
+ if detections is None:
538
+ detections = get_text(image=image, show=0, output="all", **kwargs)
539
+
540
+ for bbox, text, score in detections:
541
+ if score > thr:
542
+ top_left = tuple(map(int, bbox[0]))
543
+ bottom_right = tuple(map(int, bbox[2]))
544
+ image = cv2.rectangle(image, top_left, bottom_right, color_box, 2)
545
+ # image = cv2.putText(
546
+ # image, text, top_left, font, fontScale, color_text, thickness_text
547
+ # )
548
+ image = add_text_pil(
549
+ image, text, top_left, font_size=font_scale * 32, color=color_text
550
+ )
551
+
552
+ img_cmp = cv2.cvtColor(image, cmap)
553
+ if show:
554
+ ax.imshow(img_cmp)
555
+ ax.axis("off")
556
+ # plt.show()
557
+ return img_cmp
py2ls/plot.py CHANGED
@@ -1637,6 +1637,30 @@ def figsets(*args, **kwargs):
1637
1637
  ax.tick_params(
1638
1638
  labelsize=val
1639
1639
  ) # float, distance in points between tick and label
1640
+ if "text" in key.lower():
1641
+ if isinstance(value, dict):
1642
+ ax.text(**value)
1643
+ elif isinstance(value, list):
1644
+ if all([isinstance(i, dict) for i in value]):
1645
+ [ax.text(**value_) for value_ in value]
1646
+ # e.g.,
1647
+ # figsets(ax=ax,
1648
+ # text=[
1649
+ # dict(
1650
+ # x=1,
1651
+ # y=1.3,
1652
+ # s="Wake",
1653
+ # c="k",
1654
+ # bbox=dict(facecolor="0.8", edgecolor="none", boxstyle="round,pad=0.1"),
1655
+ # ),
1656
+ # dict(
1657
+ # x=1,
1658
+ # y=0.4,
1659
+ # s="Sleep",
1660
+ # c="k",
1661
+ # bbox=dict(facecolor="0.8", edgecolor="none", boxstyle="round,pad=0.05"),
1662
+ # ),
1663
+ # ])
1640
1664
 
1641
1665
  if "mi" in key.lower() and "tic" in key.lower(): # minor_ticks
1642
1666
  if "x" in value.lower() or "x" in key.lower():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: py2ls
3
- Version: 0.1.10.0
3
+ Version: 0.1.10.1
4
4
  Summary: py(thon)2(too)ls
5
5
  Author: Jianfeng
6
6
  Author-email: Jianfeng.Liu0413@gmail.com
@@ -1,6 +1,6 @@
1
1
  py2ls/.DS_Store,sha256=BloZZz2vlFVfF-I3X7ZsqXusvqOawJMx7erKcnIP-b0,6148
2
- py2ls/.git/COMMIT_EDITMSG,sha256=5xj-jWMbrdOc9m7gSn-UcsAQ9FMNvWSbLWSsrOUIO5w,7
3
- py2ls/.git/FETCH_HEAD,sha256=6cJaQzb6VhkvNNm-KsABX6R28FNKZB8qMmqacDEP_dQ,100
2
+ py2ls/.git/COMMIT_EDITMSG,sha256=AdtqRHle5Ej2EBNPJY79v-SB454v5UK4wuPCPFELiFQ,11
3
+ py2ls/.git/FETCH_HEAD,sha256=VM-2Jiw6iPaGu0ftg9xwq76OyNPWV0iT1nL0VWiL1zI,100
4
4
  py2ls/.git/HEAD,sha256=KNJb-Cr0wOK3L1CVmyvrhZ4-YLljCl6MYD2tTdsrboA,21
5
5
  py2ls/.git/config,sha256=CL7WR7jU8VRchJwRooCBkXUMvuRoPdf3FWIBAOlap1c,378
6
6
  py2ls/.git/description,sha256=ZzMxc0Ca26m45Twn1DDnOHqin5VHEZ9uOTBrScIXSjE,16
@@ -17,12 +17,12 @@ py2ls/.git/hooks/pre-receive.sample,sha256=pMPSuce7P9jRRBwxvU7nGlldZrRPz0ndsxAlI
17
17
  py2ls/.git/hooks/prepare-commit-msg.sample,sha256=6d3KpBif3dJe2X_Ix4nsp7bKFjkLI5KuMnbwyOGqRhk,1492
18
18
  py2ls/.git/hooks/push-to-checkout.sample,sha256=pT0HQXmLKHxt16-mSu5HPzBeZdP0lGO7nXQI7DsSv18,2783
19
19
  py2ls/.git/hooks/update.sample,sha256=jV8vqD4QPPCLV-qmdSHfkZT0XL28s32lKtWGCXoU0QY,3650
20
- py2ls/.git/index,sha256=B5IywwCaOb2OVM4Y6a-PbFrE0dw5kWJNBmS_pt5rX64,2663
20
+ py2ls/.git/index,sha256=YCofugnXLz17aY5kqXGtUpV8FzXPdgQdEwcjQ8bHDfg,4232
21
21
  py2ls/.git/info/exclude,sha256=ZnH-g7egfIky7okWTR8nk7IxgFjri5jcXAbuClo7DsE,240
22
- py2ls/.git/logs/HEAD,sha256=PxF4A3pZNQ0cmb9tDaWdAqUEQAjdzttQQPRzs_rZ2_Q,3379
23
- py2ls/.git/logs/refs/heads/main,sha256=PxF4A3pZNQ0cmb9tDaWdAqUEQAjdzttQQPRzs_rZ2_Q,3379
24
- py2ls/.git/logs/refs/remotes/origin/HEAD,sha256=VZ_d8iu4auJbbS2EiAF5JWG39Zwp02nsOZ9vTBrHyOw,23362
25
- py2ls/.git/logs/refs/remotes/origin/main,sha256=X52i-ullLIOoYAPiBN7ngbschyk88soEX4fCNjw2fGk,3040
22
+ py2ls/.git/logs/HEAD,sha256=8ID7WuAe_TlO9g-ARxhIJYdgdL3u3m7-1qrOanaIUlA,3535
23
+ py2ls/.git/logs/refs/heads/main,sha256=8ID7WuAe_TlO9g-ARxhIJYdgdL3u3m7-1qrOanaIUlA,3535
24
+ py2ls/.git/logs/refs/remotes/origin/HEAD,sha256=58xXc_oi3Gt_mWRW_ZF3IES40amGtFS1N_I8J7E85Cw,23540
25
+ py2ls/.git/logs/refs/remotes/origin/main,sha256=9ohHV9XT1dBowBZUVo52U9205_o513hmvCvtW9rS4Fk,3192
26
26
  py2ls/.git/objects/01/d5bd8065e6860c0bd23ff9fa57161806a099e1,sha256=hEQ8nqJnGsfFsuV5wc4cZas58rehXvT0v5ANx1zmMAY,584
27
27
  py2ls/.git/objects/09/08da26de58c114225ad81f484b80bf5d351b34,sha256=NOyYvrJxATpK3aDdP1_stwkqOQRDwJn7DSy6isyKImE,925
28
28
  py2ls/.git/objects/0b/409e1bc918277010f5679b402d1d1dda53e15c,sha256=y5S1XaGxJz1NXi-SPWjPC_NKIqqSbZv9oOg74MzBihY,156
@@ -36,20 +36,29 @@ py2ls/.git/objects/1d/fe9d9633b24ea560354f4f93d39c6e5f163ea0,sha256=mV_84wLqIitn
36
36
  py2ls/.git/objects/20/72c28e83f4347959d29f7b3a6c1fc3e4ee6b59,sha256=85riTUsfNmOwOoolBNgC0HegJ6LajYl5vFDl_l3W19Y,9947
37
37
  py2ls/.git/objects/24/6b368b986f758630c46dc02b7fa512b53422f7,sha256=sw7ERFCFu7m6fnURAqQfQ4GWShaARr-Vc6GRnlOPkxU,8512
38
38
  py2ls/.git/objects/25/b796accd261b9135fd32a2c00785f68edf6c46,sha256=4ic5vOwEdfbGL8oARSVEeAnSoDs14-gggGZEL-61nYE,564
39
+ py2ls/.git/objects/27/aa6074f652bc6f7078f8647489d9ee8e24f0e2,sha256=yrhSezJgUP0qjNid0oPA2Hkqpr7x89LCgw6l_FbgIqM,6057
40
+ py2ls/.git/objects/28/c2969d785c1b892c2a96b3f00eba63a59811b3,sha256=MHJ8giN9LRzVgeWEi5BhmUiCJKUc3V4JkkwcR-8NoGo,1071
39
41
  py2ls/.git/objects/2a/ae95d517d213b660bf4f65a4e0cfae7bb893eb,sha256=V0MJF1QXVSLDntEWS1n3_lErS_faBLwlP67fXv_Bysk,776
42
+ py2ls/.git/objects/2a/fdf45791a26d42ccead35ace76a8f0b2a56561,sha256=jDm8bBdAgNEDk0XteXGVrEItc1mRaaFWP6hz62UZQnU,267
40
43
  py2ls/.git/objects/30/a2f8da47ee947811dc8d993f5a06a45de374f4,sha256=u5W33_qNtTs1-U8Fardx-zB_udqKvuCm5kiw1mQGdsU,3218
41
44
  py2ls/.git/objects/32/fd627b62fad7cf3b2f9e34ab9777126a0987ad,sha256=_QlClFT2799H_igDlGPr6Uz3SqoPN5v-hehesdIj18U,164
42
45
  py2ls/.git/objects/34/9e31b6a3634cea102ce5588b98c11cc1738605,sha256=drNtu4yQrw0rWqk6SmHnsW0ZA3Jtvut8WG9S15gyTwk,1219
46
+ py2ls/.git/objects/34/b6f3a2ee84f39bed4eee57f2c0e0afb994feb1,sha256=HdtFToCoX-g2trQ8m46yvEjeitUYmGlyZ3rqQIEHNwg,17992
47
+ py2ls/.git/objects/35/1a5f491ab97eee9d1ee699478d75a8bb5d3dc2,sha256=80vRDvQZlx4WfCSgJBgR-MNYmt1fCq5N4-3By_7hGrM,63106
43
48
  py2ls/.git/objects/36/b4a1b7403abc6c360f8fe2cb656ab945254971,sha256=X18sHo17gQTxusNptoILoWxSfU0ulrsZvbETlBtV5aY,2327
44
49
  py2ls/.git/objects/36/e56a361f526eafa59c5235a5c990bf288b5f9c,sha256=7L1L-iqVvuufrlfEE2myD0-QnAPueFMySKetu08W-Pc,34216
45
50
  py2ls/.git/objects/36/ef43e50009e59db11812c258846d9e38718173,sha256=0nwCwQSuQAdGyD2NfEK-_L12ydE9nGVKBXOfFq_Lndc,169
46
51
  py2ls/.git/objects/39/7ead045fbbcfb17c62019eb18fe21ed05dbee5,sha256=3zM2AAtKWPfDVSy-prLogf1Z_RjkA6DwBBvN_M7pZqs,10409
52
+ py2ls/.git/objects/39/b13be65125556784e44c7a1d9821703c7ab67e,sha256=Jm8jhOfT8XB6DRkEynhEa1QMDHl-HIKW0Zr-goqSV8E,822
53
+ py2ls/.git/objects/3b/507acc7f23391644cc0b824b1e79fd2677a362,sha256=p-F6SaVzlGTr4u44DaqxAqVF-doxhEpiM8nXwqdMUp0,53351
47
54
  py2ls/.git/objects/3b/bd972aa7ad680858f8dfbd0f7fcd97756f0d6f,sha256=MQWEzML3wbb4GixiHDCHrxgbXLQitrYDstT1plhmQSU,169
48
55
  py2ls/.git/objects/3c/bbe5f4173d165127b9ad96119f1ec24c306ffc,sha256=S1BXemROYtzRaj5WXLPYnTmPTBQDKovMEN0GRLul-I4,33489
56
+ py2ls/.git/objects/3d/9d10d27724657a436c65a6254bfd213d4b3562,sha256=k70wNeQBm9RHdMlSQa-4HU2Gi50k0Fpa3O_3n0wmp_A,1063
49
57
  py2ls/.git/objects/3f/d6561300938afbb3d11976cf9c8f29549280d9,sha256=91oqbTWfUE1d_hT_1ptYmRUb5pOQ1X4oxQxpF6NXjKU,8501
50
58
  py2ls/.git/objects/41/dcf4b3bf0460946b2da93776cf9e836d62178f,sha256=jdsIHuNTgeely4JL072ktLgpDHB-97GDL7unti8TLrw,93
51
59
  py2ls/.git/objects/43/dbd49b2ee367c5434dd545e3b5795434f2ef0b,sha256=DAzt0dWp2KsuuImCKp7N9ia7KaCDNqwB-tYIx3Wf_c0,565
52
60
  py2ls/.git/objects/45/b1b6178bacbfc997811a998b5cc60c1ea7fac8,sha256=7Oea5ZA0nNs6-jQV0yCSr4ZSCcm7m_z3UYVLNmDBj2w,167
61
+ py2ls/.git/objects/47/6cbd5a7c5e35cddef2f8a38bdc4896d403b095,sha256=Egdg9CCOcB_IWt3CBaIpmdZzBGK4woUYY5v5X275trc,207
53
62
  py2ls/.git/objects/48/a88fc5806305d0bb0755ee6801161b79696972,sha256=f3JStE39k_hPGE-WRwqZtDTjQkfOmBVb_6-ELBbScjI,203
54
63
  py2ls/.git/objects/4f/7afb40dff2153d857fc85748c2eecb85125042,sha256=QnSXlNWzKLoMzDHNAiwe06vqJEQj9xu0q-9PvCUbtbM,39680
55
64
  py2ls/.git/objects/50/08ddfcf53c02e82d7eee2e57c38e5672ef89f6,sha256=p0M2WLqiTe6X2FI_k5Aj0IEsE85jqLa58sVdmV8x1vU,255
@@ -79,17 +88,23 @@ py2ls/.git/objects/6d/ee29dbdcc84edeeacede105110446f3ccac963,sha256=-4Bi-tw0O-Eb
79
88
  py2ls/.git/objects/71/36b2074a2754be8b58127d82250e5b37e3c373,sha256=cbVFQaBx0Q5QkZ1wQle-iIxNx14JxGSx3G8aQ7EbbAA,586
80
89
  py2ls/.git/objects/72/245a05b0966011cb381e6b32b0465000e969ab,sha256=Jph97UOQ6ZXwAEIZHkd037rPpoj_hbs74kMIj1nmIVc,14013
81
90
  py2ls/.git/objects/72/e4179337639859678ddaecf38b16f33aaec8e1,sha256=bdgg9rmPgKP3UH1-QDqIrCgzmrFVRgdByrgYuQkzVJg,436
91
+ py2ls/.git/objects/78/063f4c863fc371ec0313303c0a81283b35d9b6,sha256=ow4MGqzfJbPrW9ggXw2Eh4JwCjteFWNscgR_SuyMUR4,2280
82
92
  py2ls/.git/objects/78/3d4167bc95c9d2175e0df03ef1c1c880ba75ab,sha256=SK2QDjDBiDhVMG1I5p19g4RbEm2Rax7mYnxawmVZYxs,15523
83
93
  py2ls/.git/objects/79/7ae089b2212a937840e215276005ce76881307,sha256=lQOKF2pb1JvipI3eT79X0-TuMGWsy1A-Yw4BCgKZNOM,33472
84
94
  py2ls/.git/objects/7e/5956c806b5edc344d46dab599dec337891ba1f,sha256=sfqJBiSNj-gyJo4D7xkmRAo76mC2ztjqeZZsl4ifULA,162
85
95
  py2ls/.git/objects/81/8f26b7bf042269729020cf944fc362d66ba27e,sha256=mg6FGEyv6EcOgurR8CEvHGovaWrUgMUxTtACAy7-ei4,34960
96
+ py2ls/.git/objects/82/70b319ce4046854fbe7dc41054b6c2d112dab2,sha256=uXFGUHmm5K4cACXpWG_hQN4xV__3WxFyeKipRpvXAxo,27520
86
97
  py2ls/.git/objects/84/59071b722a255b774a80b27746033f8141ab39,sha256=0pYGJOXFfp4MSu4n5MzE1XN--t0lSs7wcdqboADWMx0,9792
98
+ py2ls/.git/objects/85/aee46f478e9afdb84d50a05242c53b04ed2e21,sha256=R1IE6mh9ANSTSzouKl5BclPeIR_ubjFnXBI4xPRvGRo,23087
99
+ py2ls/.git/objects/86/e288b46f8fe179907e4413f665aeb5053fddb1,sha256=uIVROSpqiSyImHBLh0X9bSpKl6zveGGoJSzO8W799QE,160
87
100
  py2ls/.git/objects/87/ef1fc3f7f1ddc4d0ab9b3e65381ce9f3388621,sha256=OFrpW6lu31qGBvD3ijPUBSG9JrdU1_mKzeYBzidn9VM,3748
88
101
  py2ls/.git/objects/8b/84f56978e1de8f2ae82abce5f8b3e182d365cd,sha256=a8XequnUMBSv9zIQJdcdgDvMQ7PLGdIrgZ-MqQGF87c,573
89
102
  py2ls/.git/objects/8e/55a7d2b96184030211f20c9b9af201eefcac82,sha256=yW-jVYeCTWR-nX3JJgA1g9YLPjzNsKlDmEOH290Ywx0,1221
90
103
  py2ls/.git/objects/91/c69ad88fe0ba94aa7859fb5f7edac5e6f1a3f7,sha256=Kk2MWCO1OcShYuABGzp2O9LiWGDfDkcZtd0oy4nY6RU,9529
91
104
  py2ls/.git/objects/94/74152b4b463d70ae5ad07f0c658be3e296026b,sha256=jmA6qTuUVldsyjg5Z0WddbnSm7GrqWug3USa16jifo4,3733
105
+ py2ls/.git/objects/94/f7dbe88e80c4205a901b71eb8f181974376bba,sha256=eqJuPAKPX35xx17vtfrw_MtLgRwbV5q_txWBZAwPZRY,377
92
106
  py2ls/.git/objects/97/1aef09ea939f46b60b9646f8d524c78a9220f4,sha256=lWu9FuqxnDT-H_jZGYlA0Tbx6NdH7bScu5EVl3BoAxU,2532
107
+ py2ls/.git/objects/9b/ec5ee2236ee2d5532c36bfd132e23c58fdb69c,sha256=r3oCOgc2gGRPNuTEB8PkqGt6YNZtGfbwwutxzqAQN5k,20191
93
108
  py2ls/.git/objects/9d/0df52899fe95279059286d9c0ec42287edc168,sha256=67nV3TLo-fwe4lt0wwvxoDnVNHc1IpapRyAY2STP3iI,564
94
109
  py2ls/.git/objects/a1/5389729850729fc7bd78a54f26fce77f30be12,sha256=iNB4jWPKwQfHJSwbGiXz7UgC5J-LbLZu19ylWNr2COs,159
95
110
  py2ls/.git/objects/a1/906da89d1174f74867800c74c43af36253bd5e,sha256=vKUcX4zLVSvZ2Z_FXP-1czZF9MkVMsFidtg43CEhxkQ,780
@@ -99,52 +114,65 @@ py2ls/.git/objects/a7/3e13eafee65c5b8d73ad2d3ea46d0eee82f0d3,sha256=iv3uTzna5XBz
99
114
  py2ls/.git/objects/b0/56be4be89ba6b76949dd641df45bb7036050c8,sha256=8Y7z30eNceBd5QIx09QfMp5cYBbrgUllmats0kvJEJ4,132
100
115
  py2ls/.git/objects/b0/9cd7856d58590578ee1a4f3ad45d1310a97f87,sha256=82dx4hIdMpdcB64e5PU1s2gZFVkTvrj1cPwwJ_kasNU,4444
101
116
  py2ls/.git/objects/b2/18e6a0f0f1c4df8cdefa9852058348abc713b7,sha256=hOQfdyzDZctjoge0-pAcEDel5XHVPNfOtrMNyFPUOIE,564
117
+ py2ls/.git/objects/b3/4f7f271c6d6105e35a6556ffda71d03afe8c96,sha256=dpXOxJ_H0pMun5XcAenyvzwEeeRh0FluxyIHYYBc1HI,1046
118
+ py2ls/.git/objects/b3/69579064bde9de9a19d114fc33e4e48cc8c0e4,sha256=nC24lde8O_cg64f6QjMazlPTHX9X4aSoEBwbdXWTh_Q,62664
102
119
  py2ls/.git/objects/b5/61831c7dce8ea51e7ee6b6fa35745f14d8242d,sha256=wUqxlKjLN1vOUj2tkYStado64QewdcF3CHlSICds1ik,34415
103
120
  py2ls/.git/objects/b7/2c9e75ab7d0afe594664650aa8f6c772f5ac64,sha256=dyeWYp22wgZSCE7D3F43N76ehCDTsbMJcSMJRW3VbDI,65
104
121
  py2ls/.git/objects/bb/81ccc0513f18fc160b54a82861e9a80d23f4f6,sha256=WrBnpacpm4kOcVCYoWgPO8MqOAi0ZeHaxekPT3DxpCk,587
105
122
  py2ls/.git/objects/bb/934eb33bc1a8b85630bf680caffd99560c1b8f,sha256=ggehjexUsWlskHJvHxW7u6U0otB0OCItmIZdT9O-3OU,9670
106
123
  py2ls/.git/objects/bf/67907e337021ebff434e02b19b30a741c144af,sha256=4nCdIiXB2kZUL2n0U-cTeGCXlFrlI3uCr3QDytICFOE,157
124
+ py2ls/.git/objects/bf/b54d65922ce1dfda1aaa014913a54e7172d0bc,sha256=ng8_i6LZEhJTkpZxRThCVYr9Z41F-DJ286vC0CGQaFs,61371
107
125
  py2ls/.git/objects/c1/20fc812b9ad311c34a3608512d6a9d976bb48e,sha256=q-WAKugB-_-g7w0Mlw6oyTBaXQ_Qd7BdLatrDiYN7Wc,156
126
+ py2ls/.git/objects/c1/397c6ed72c4e20ef6b9ab83163e9a6baba5b45,sha256=f55lzMy77ERzLOEUAYsrbxY9rUQCkuZrKo8UFayEdmQ,15861
108
127
  py2ls/.git/objects/c4/cba65f1163661999ee4b8ed23342b63bc1300c,sha256=rwSdKt-C98nUQ_B-7imY4fYRYmn29MQc4SIu9wruHeo,566
109
128
  py2ls/.git/objects/c6/7f17e5707313600efcb85e9a3fedea35dba591,sha256=TL7rDIWiaWlk8iIwqPst7St5Xr2otPs-vp17GPlET7o,565
110
129
  py2ls/.git/objects/c6/f32aced880bd165a251cb52b26b0c1107e2141,sha256=ybarsWjoo-JCb8gnxsG6VKt6Sal2VwQiOEZYxQl8DcE,29556
130
+ py2ls/.git/objects/cc/45df1d317a2eb63ff1ff3a5f3b4a9f98fd92b5,sha256=GEEKOqrl6bLBn1z6B8uhjY7uGOWP6TELfBEmx41vdVE,1041
111
131
  py2ls/.git/objects/cd/822b3574a88ebdd1ed82fd6983f37e626d52b4,sha256=MJbPJ-8vpfVTUMhzyzTp5qGmZYgsgrG-7nwwzDdODlQ,617
112
132
  py2ls/.git/objects/cf/0c0d9c6fb09473aaeb7f7e2edbd770c3f2ef3d,sha256=T_nV0GrgpVu3mOJ4fYcCW98oCunzgqy0DnSX0luy04Q,183
113
133
  py2ls/.git/objects/d2/992df305f4b56a466a2f221aeb182ddd20f418,sha256=6zInwArNx-uw1OzxOIhTAZiKpelI3cyhW7Z-dj-Ws2I,9546
134
+ py2ls/.git/objects/d6/39e8af592cd75a318d8affddd1bcc70c2095f2,sha256=WwoBGl56IGGKozgK1u046NjWMkrqebL2HKm3p9mpl9Y,1029
114
135
  py2ls/.git/objects/d6/9ab1c4aadf279936dd778e8346ba60f74705b6,sha256=WcfdSMKqfiWT5TOWVUcDj0XDaD2hYxDnyIRNlYGutL8,34976
115
136
  py2ls/.git/objects/d8/4688b54c0040a30976b3a6540bc47adf7ce680,sha256=1gJp1iTVAooc5PZZsiIj215-J1RtJ-_zy22_9jZ8jAY,160
116
137
  py2ls/.git/objects/d9/005f2cc7fc4e65f14ed5518276007c08cf2fd0,sha256=IJIoz93V7pf9yx43U1JdN8gBq_LWtw8A9Z2YMPnq_B0,1450
117
138
  py2ls/.git/objects/d9/c2403fd166ce791b4e9d0c6792ed8342c71fcd,sha256=uD7BsKdrmN-9FStTpwsRWh-XxVXeDsV4dGjFkaMIIs8,170
118
139
  py2ls/.git/objects/d9/dfa5aee51e92a541b707e8e7baea6f06deff98,sha256=jMdhZ1i_L5q_UgjOtjLN15PCSCz3pE51FhD3z74ZUr8,163
119
140
  py2ls/.git/objects/db/141dbaa93594df2a8156182f361ee4db829359,sha256=TpKTLvbDc4Blzrp1Pq9JijqDROJyBJ7sCQQBmIuYKZo,845984
141
+ py2ls/.git/objects/db/3f2cd643292057936230b95cf7ec3046affe11,sha256=EdzFq6kPwS2lvqY0OVBi9QV-O5LlhX7K11XZn58v4NQ,27259
120
142
  py2ls/.git/objects/db/ffa8ea7bda721d0cee7b9e4ce5b2ef927733ff,sha256=GhDkvP6JYV26qVg5ETPys1ZEnGlsct9hiXCc24Ky4Xg,565
121
143
  py2ls/.git/objects/dc/c2bdbafb3296e09d9ee4955cfa55d275825f94,sha256=EwQROBwhbTP2VbODehfMwXhSoDC1LknvxBtDkCOCTco,988
122
144
  py2ls/.git/objects/dc/cdbd4266765d840be2ae35ab1752a0fa312c16,sha256=ABBvXL3TWBRyx_q5XQLgg-k0EU2rvMKv3HIXpk73I8c,12583
123
145
  py2ls/.git/objects/dd/87fb5f606fe380d81e6fe3a2c98f9f99e3e09b,sha256=gBHzzOxwRhDCCwm6WWSyN51RSENJhwEojekygZj8qsc,1424
146
+ py2ls/.git/objects/de/214c626ac2dd2685bfaa0bc0fc20f528d014d7,sha256=NwSO-vjNC-cdecDSkfIobxW8Ap-L5QspDr_QnyVyBKI,1070
124
147
  py2ls/.git/objects/df/e0770424b2a19faf507a501ebfc23be8f54e7b,sha256=vCdlxwEidekh8i-5TVMVgSLGk9DPZCZAbWqvGYSKQ9c,76
125
148
  py2ls/.git/objects/e2/f2f8f4c25e62a297fc55f36acc6b01cfbab76f,sha256=OnyaP4aCH8kHPj2ZcRsBnUQOGYLmX0OOG8uYtDClmB8,113
126
149
  py2ls/.git/objects/e3/1356f90ea6dd0577b5e0b40b206319adcbf085,sha256=I9_QNwmmtoqSwq29Ixdfv_PgF2x14u2M6sX1eQumwoY,161
127
150
  py2ls/.git/objects/e3/5a4dafc50850cacac7bf76c56db2715cbda2c4,sha256=GAcBj3YSEbm6tm7fGD6al16uBo8LtEtjZ2Hi-UgIsUg,3290
151
+ py2ls/.git/objects/e4/6c715352db9fe3c887a635f1916df4ca1f4ff9,sha256=gH66Mpwns2PXEgZ0iqVgTYXGOuAnblKTVnWPsPntIug,825
152
+ py2ls/.git/objects/e5/0580a0bd1e1b3d29f834382b80fceb61d5cf0c,sha256=4QxUSgpyDtN4XeWOoFFAQ_c8FYbs3BJaOyr5ErXtZkM,434
128
153
  py2ls/.git/objects/e9/391ffe371f1cc43b42ef09b705d9c767c2e14f,sha256=RWTy2n8L2XxZQknBFyPczA0Aa_4gSG_Ybcr8e8v4ccc,10264
129
154
  py2ls/.git/objects/ea/3a18cc75e53792744ef754e05d3f4481768c13,sha256=p-Ny3qYi1akV2i58-Fxr0c0c8lhaEVrbr6ek7-v21jM,9369
130
155
  py2ls/.git/objects/ec/40fd8bf8e4c342534a9fc020289e402ba6bc9d,sha256=ptkoZH3jOT5PLgsYhxSutkPvtJ4TUlcD5H5JVJIu_AA,28992
156
+ py2ls/.git/objects/ec/d980279432b13f0374b90ca439a6329cdece0f,sha256=l-PpV7ZbOPp1QVROl_R9UANx58pNevZTeX8hUoJUuU0,10313
157
+ py2ls/.git/objects/ee/cee64eacaff022dcdc509c0c2b1da492f21060,sha256=KfPSbfpy1b-1-93aEh27gddBCJ3GJMnTQeP78ZE97ok,51197
131
158
  py2ls/.git/objects/f1/e50757fddc28b445545dc7e2759b54cdd0f42e,sha256=2NG4lzk2IPOZfJ4tRHvxla63yQTcY_YTOprG1tzK-IY,40554
132
159
  py2ls/.git/objects/f4/b64d3107b39e3ad6f540c6607004ea34e6c024,sha256=0egAtqc0x8hc7U1z91tIjcRhSd_BT2a_gxZxo_7NTJA,564
133
160
  py2ls/.git/objects/f4/ba7f815b886797b73fede071d86e0c134d2bc7,sha256=UispRfx4EvDFujmyICYgMz4gZHqZhO_dbNfiL3MFTNw,332
161
+ py2ls/.git/objects/f5/61c3c1bf1c9ea9c9d1f556a7be2869f71f3bdf,sha256=ceHjRx-BTwl4FdgHFykZTGT_hoertmCG1z0aY-9FEH8,29789
134
162
  py2ls/.git/objects/f6/44a8ff56fa035105fc517cbb1ac46c3d8e349a,sha256=a_5p_uf5YsJVFzXNWAAQptZr8l7rGNLLxasotb9q0OU,777
135
163
  py2ls/.git/objects/f7/c98ba5c2f903e603b1f5e63d49fbc8a43815cc,sha256=tYbi3A7irrIPB_11bwItuof0Vc9a0MDuLFMNAzRsG3A,33467
136
164
  py2ls/.git/objects/f9/045a08e96eb76848fc4d68e3e3e687cca39a2d,sha256=u29Cwu3SL3HlagZIal5ueGZ3miqgZAtuDbtP8-fUVvE,141
137
165
  py2ls/.git/objects/fa/147e6bb78a2e8db241d231295fd7f1ed061af8,sha256=G9pg5LXv7AdxnPIQsTm2AF3Un314dLRJQYwxmZem9rQ,574
138
166
  py2ls/.git/objects/fc/292e793ecfd42240ac43be407023bd731fa9e7,sha256=hGIYoxKWNT3IPwk3DE4l3FLBbUYF-kXcHcx7KrH9uS0,1971
139
- py2ls/.git/refs/heads/main,sha256=3R7BfzmwaGeSA28_orvfyaZoX75EId2y8ANpCMQo6TM,41
167
+ py2ls/.git/refs/heads/main,sha256=vaWoJWq2YOJm04yq8v-ImmaNjjqq-OEhS8SdTKUuKw8,41
140
168
  py2ls/.git/refs/remotes/origin/HEAD,sha256=K7aiSqD8bEhBAPXVGim7rYQc0sdV9dk_qiBOXbtOsrQ,30
141
- py2ls/.git/refs/remotes/origin/main,sha256=3R7BfzmwaGeSA28_orvfyaZoX75EId2y8ANpCMQo6TM,41
169
+ py2ls/.git/refs/remotes/origin/main,sha256=vaWoJWq2YOJm04yq8v-ImmaNjjqq-OEhS8SdTKUuKw8,41
142
170
  py2ls/.gitattributes,sha256=Gh2-F2vCM7SZ01pX23UT8pQcmauXWfF3gwyRSb6ZAFs,66
143
171
  py2ls/.gitignore,sha256=y7GvbD_zZkjPVVIue8AyiuFkDMuUbvMaV65Lgu89To8,2763
144
172
  py2ls/LICENSE,sha256=UOZ1F5fFDe3XXvG4oNnkL1-Ecun7zpHzRxjp-XsMeAo,11324
145
173
  py2ls/README.md,sha256=CwvJWAnSXnCnrVHlnEbrxxi6MbjbE_MT6DH2D53S818,11572
146
174
  py2ls/__init__.py,sha256=Nn8jTIvySX7t7DMJ8VNRVctTStgXGjHldOIdZ35PdW8,165
147
- py2ls/batman.py,sha256=CSM3PxXE4Qpt_yVfaAgMUAGZuJCG_PGkENjG_Mjev4k,5717
175
+ py2ls/batman.py,sha256=_DKFjLsX4s6F5wbWcogLbOykE0Xx79C2W8oAvisaPEg,6218
148
176
  py2ls/brain_atlas.py,sha256=w1o5EelRjq89zuFJUNSz4Da8HnTCwAwDAZ4NU4a-bAY,5486
149
177
  py2ls/chat.py,sha256=Yr22GoIvoWhpV3m4fdwV_I0Mn77La346_ymSinR-ORA,3793
150
178
  py2ls/correlators.py,sha256=RbOaJIPLCHJtUm5SFi_4dCJ7VFUPWR0PErfK3K26ad4,18243
@@ -178,14 +206,15 @@ py2ls/doc.py,sha256=xN3g1OWfoaGUhikbJ0NqbN5eKy1VZVvWwRlhHMgyVEc,4243
178
206
  py2ls/export_requirements.py,sha256=x2WgUF0jYKz9GfA1MVKN-MdsM-oQ8yUeC6Ua8oCymio,2325
179
207
  py2ls/freqanalysis.py,sha256=F4218VSPbgL5tnngh6xNCYuNnfR-F_QjECUUxrPYZss,32594
180
208
  py2ls/ich2ls.py,sha256=3E9R8oVpyYZXH5PiIQgT3CN5NxLe4Dwtm2LwaeacE6I,21381
181
- py2ls/ips.py,sha256=HbktFzKIszBHtB3DtyUCCM6xj9NJZAz38ZCcIomjBFs,105439
182
- py2ls/netfinder.py,sha256=xma9YoBxY4GcgoyG4YXEOU8oKPYByIt0uWqPyshHt8s,50812
183
- py2ls/plot.py,sha256=9z0VPvMTFsuYKakuHjTGKK6UtiQylM3-WCxbNEKxTos,95283
209
+ py2ls/ips.py,sha256=DzUlb6LYFj6fC58cU1FaLCh5PkAC3VCijzXyhbPMcVA,105440
210
+ py2ls/netfinder.py,sha256=kJT78g9L0NWkcbPMsnY_2d7gQdwMKxfQ3ojfZm2-M00,54373
211
+ py2ls/ocr.py,sha256=qx1HjiWGpteHIV8SzDavAnxfG9AGAxJySo1IeNvNwrg,19765
212
+ py2ls/plot.py,sha256=J8hRKLpQXHQRG_xE_nmT0mQvc1IxCMJ21tJmKsUKFl4,96155
184
213
  py2ls/setuptools-70.1.0-py3-none-any.whl,sha256=2bi3cUVal8ip86s0SOvgspteEF8SKLukECi-EWmFomc,882588
185
214
  py2ls/sleep_events_detectors.py,sha256=bQA3HJqv5qnYKJJEIhCyhlDtkXQfIzqksnD0YRXso68,52145
186
215
  py2ls/stats.py,sha256=fJmXQ9Lq460StOn-kfEljE97cySq7876HUPTnpB5hLs,38123
187
216
  py2ls/translator.py,sha256=bc5FB-wqC4TtQz9gyCP1mE38HqNRJ_pmuRIgKnAlMzM,30581
188
217
  py2ls/wb_detector.py,sha256=7y6TmBUj9exCZeIgBAJ_9hwuhkDh1x_-yg4dvNY1_GQ,6284
189
- py2ls-0.1.10.0.dist-info/METADATA,sha256=76w1Clumy3D_fhABBpVwMaJV_49n-MJnOVRHW3wiaJY,20018
190
- py2ls-0.1.10.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
191
- py2ls-0.1.10.0.dist-info/RECORD,,
218
+ py2ls-0.1.10.1.dist-info/METADATA,sha256=BjfmIp8s1DEklNcXqKNCxp5lLV13iUKjZSzMc_WadrU,20018
219
+ py2ls-0.1.10.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
220
+ py2ls-0.1.10.1.dist-info/RECORD,,