py2ls 0.1.10.25__py3-none-any.whl → 0.1.10.26__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.
- py2ls/batman.py +57 -28
- py2ls/data/email/email_html_template.html +88 -0
- py2ls/ips.py +20 -11
- {py2ls-0.1.10.25.dist-info → py2ls-0.1.10.26.dist-info}/METADATA +7 -2
- {py2ls-0.1.10.25.dist-info → py2ls-0.1.10.26.dist-info}/RECORD +6 -5
- {py2ls-0.1.10.25.dist-info → py2ls-0.1.10.26.dist-info}/WHEEL +0 -0
py2ls/batman.py
CHANGED
@@ -68,7 +68,7 @@ def extract_kv(
|
|
68
68
|
else:
|
69
69
|
if "win" in sys.platform:
|
70
70
|
dir_data = (
|
71
|
-
"Z:\\Jianfeng\\Apps
|
71
|
+
"Z:\\Jianfeng\\Apps\\settings\\confidential_data\\gmail_login.json"
|
72
72
|
)
|
73
73
|
elif "lin" in sys.platform:
|
74
74
|
dir_data = "/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
|
@@ -86,23 +86,30 @@ def email_to(**kwargs):
|
|
86
86
|
def email(**kwargs):
|
87
87
|
"""
|
88
88
|
usage:
|
89
|
-
email_to(who="example@gmail.com",
|
90
|
-
|
91
|
-
|
92
|
-
|
89
|
+
email_to(who=["example@gmail.com"],
|
90
|
+
subject="test",
|
91
|
+
what="this is the body",
|
92
|
+
attachments=["/Users/test.pdf"],
|
93
|
+
cc=["cc1@example.com"],
|
94
|
+
bcc=["bcc@example.com"],
|
95
|
+
html_template="template.html")
|
93
96
|
Send an email with optional attachments.
|
94
97
|
|
95
|
-
:param who: Recipient email address
|
98
|
+
:param who: Recipient email address (string or list)
|
96
99
|
:param subject: Email subject
|
97
|
-
:param what: Email
|
100
|
+
:param what: Email body (can be HTML or plain text)
|
98
101
|
:param attachments: List of file paths to attach
|
102
|
+
:param cc: List of CC email addresses
|
103
|
+
:param bcc: List of BCC email addresses, "blind carbon copy"接受者之间互相看不到
|
104
|
+
:param html_template: Path to an HTML file for the email body
|
99
105
|
"""
|
100
106
|
who, what, subject, signature = None, None, None, None
|
101
107
|
attachments = False
|
102
108
|
pause_sec = False # default 10 seconds pause
|
103
|
-
|
109
|
+
bcc, cc = [], []
|
104
110
|
signature_styles = extract_kv(idx=1)[1] # signature list
|
105
111
|
signature = signature_styles[0] # default signature,None
|
112
|
+
read_receipt = False
|
106
113
|
verbose = True
|
107
114
|
if not kwargs:
|
108
115
|
print(
|
@@ -110,42 +117,59 @@ def email(**kwargs):
|
|
110
117
|
)
|
111
118
|
return None
|
112
119
|
# params config
|
120
|
+
for k, v in kwargs.items():
|
121
|
+
if any([i in k.lower() for i in ["who", "whe", "to", "@"]]): # 'who' or "where"
|
122
|
+
if isinstance(v, list):
|
123
|
+
who = v
|
124
|
+
else:
|
125
|
+
who = [v]
|
113
126
|
for k, v in kwargs.items():
|
114
127
|
if any(
|
115
|
-
[
|
116
|
-
|
117
|
-
"whe" in k.lower(),
|
118
|
-
"to" in k.lower(),
|
119
|
-
"@" in k.lower(),
|
120
|
-
all(["@" in k.lower(), "." in k.lower()]),
|
121
|
-
]
|
122
|
-
): # 'who' or "where"
|
123
|
-
who = v
|
124
|
-
if any(["sub" in k.lower(), "hea" in k.lower()]): # 'subject', 'header'
|
128
|
+
[i in k.lower() for i in ["sub", "hea", "top"]]
|
129
|
+
): # 'subject', 'header','topic'
|
125
130
|
subject = v
|
126
131
|
if any(
|
127
|
-
[
|
132
|
+
[i in k.lower() for i in ["wha", "con", "bod"]]
|
128
133
|
): # 'what','content','body'
|
129
134
|
what = v
|
130
|
-
if any([
|
135
|
+
if any([i in k.lower() for i in ["att", "fil"]]):
|
131
136
|
attachments = v # optional
|
132
|
-
if any(
|
133
|
-
["paus" in k.lower(), "tim" in k.lower(), "delay" in k.lower()]
|
134
|
-
): # 'attachments', 'file'
|
137
|
+
if any([i in k.lower() for i in ["paus", "tim", "delay"]]):
|
135
138
|
pause_sec = v # optional
|
136
139
|
if any(["sig" in k.lower()]): # 'attachments', 'file'
|
137
140
|
signature = v # optional
|
138
141
|
if not isinstance(v, str):
|
139
|
-
print(signature_styles)
|
140
142
|
signature = signature_styles[v]
|
141
|
-
if any([
|
143
|
+
if any([i in k.lower() for i in ["verb", "show"]]): # 'verbose', 'show'
|
142
144
|
verbose = v # verbose
|
143
|
-
|
145
|
+
if any(["cc" in k.lower() and "bcc" not in k.lower(), "ward" in k.lower()]):
|
146
|
+
if isinstance(v, list):
|
147
|
+
cc = v
|
148
|
+
else:
|
149
|
+
cc = [v]
|
150
|
+
if any([i in k.lower() for i in ["bcc", "blind", "secret"]]):
|
151
|
+
if isinstance(v, list):
|
152
|
+
bcc = v
|
153
|
+
else:
|
154
|
+
bcc = [v]
|
155
|
+
if any(["temp" in k.lower(), "html" in k.lower()]):
|
156
|
+
# Load HTML template if specified
|
157
|
+
if isinstance(v, str) and v.endswith("html"):
|
158
|
+
with open(kwargs["html_template"], "r") as f:
|
159
|
+
what = f.read()
|
144
160
|
# Create email message
|
145
161
|
message = MIMEMultipart()
|
146
162
|
message["From"] = extract_kv()[0]
|
147
|
-
|
163
|
+
print(who)
|
164
|
+
message["To"] = ", ".join(who)
|
165
|
+
|
166
|
+
if len(who) == 1:
|
167
|
+
who = ", ".join(who)
|
168
|
+
|
148
169
|
message["Subject"] = subject
|
170
|
+
# Add CC addresses if provided
|
171
|
+
if cc:
|
172
|
+
message["Cc"] = ", ".join(cc) # Join CC addresses as a comma-separated string
|
149
173
|
|
150
174
|
# the eamil's body
|
151
175
|
if what is None:
|
@@ -209,7 +233,12 @@ def email(**kwargs):
|
|
209
233
|
with smtplib.SMTP("smtp.gmail.com", 587) as server:
|
210
234
|
server.starttls()
|
211
235
|
server.login(extract_kv()[0], extract_kv()[1])
|
212
|
-
|
236
|
+
if any(bcc):
|
237
|
+
recipient_list = who + cc + bcc # Include all recipients
|
238
|
+
server.sendmail(extract_kv()[0], recipient_list, message.as_string())
|
239
|
+
else:
|
240
|
+
server.sendmail(extract_kv()[0], who, message.as_string())
|
241
|
+
|
213
242
|
if verbose:
|
214
243
|
if attachments:
|
215
244
|
print(
|
@@ -0,0 +1,88 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>Email Template</title>
|
7
|
+
<style>
|
8
|
+
body {
|
9
|
+
font-family: Arial, sans-serif;
|
10
|
+
margin: 0;
|
11
|
+
padding: 0;
|
12
|
+
background-color: #f4f4f4;
|
13
|
+
}
|
14
|
+
.email-container {
|
15
|
+
max-width: 600px;
|
16
|
+
margin: 0 auto;
|
17
|
+
background-color: #ffffff;
|
18
|
+
padding: 20px;
|
19
|
+
border-radius: 8px;
|
20
|
+
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1);
|
21
|
+
}
|
22
|
+
.email-header {
|
23
|
+
background-color: #4CAF50;
|
24
|
+
color: #ffffff;
|
25
|
+
padding: 10px;
|
26
|
+
text-align: center;
|
27
|
+
border-radius: 8px 8px 0 0;
|
28
|
+
}
|
29
|
+
.email-body {
|
30
|
+
padding: 20px;
|
31
|
+
font-size: 16px;
|
32
|
+
color: #333333;
|
33
|
+
line-height: 1.5;
|
34
|
+
}
|
35
|
+
.email-footer {
|
36
|
+
background-color: #f4f4f4;
|
37
|
+
color: #888888;
|
38
|
+
padding: 10px;
|
39
|
+
text-align: center;
|
40
|
+
font-size: 12px;
|
41
|
+
border-radius: 0 0 8px 8px;
|
42
|
+
}
|
43
|
+
a {
|
44
|
+
color: #4CAF50;
|
45
|
+
text-decoration: none;
|
46
|
+
}
|
47
|
+
a:hover {
|
48
|
+
text-decoration: underline;
|
49
|
+
}
|
50
|
+
.button {
|
51
|
+
background-color: #4CAF50;
|
52
|
+
color: #ffffff;
|
53
|
+
padding: 10px 20px;
|
54
|
+
text-align: center;
|
55
|
+
display: inline-block;
|
56
|
+
margin: 10px 0;
|
57
|
+
border-radius: 4px;
|
58
|
+
text-decoration: none;
|
59
|
+
font-size: 16px;
|
60
|
+
}
|
61
|
+
.button:hover {
|
62
|
+
background-color: #45a049;
|
63
|
+
}
|
64
|
+
</style>
|
65
|
+
</head>
|
66
|
+
<body>
|
67
|
+
<div class="email-container">
|
68
|
+
<div class="email-header">
|
69
|
+
<h1>Welcome to Our Newsletter</h1>
|
70
|
+
</div>
|
71
|
+
<div class="email-body">
|
72
|
+
<p>Dear Subscriber,</p>
|
73
|
+
<p>Thank you for signing up for our newsletter! We are excited to keep you updated with the latest news, promotions, and events. Here’s what you can expect in our upcoming editions:</p>
|
74
|
+
<ul>
|
75
|
+
<li>Exclusive promotions and discounts</li>
|
76
|
+
<li>Updates on our latest products</li>
|
77
|
+
<li>Invitations to special events</li>
|
78
|
+
</ul>
|
79
|
+
<p>Stay tuned for more exciting updates. In the meantime, feel free to visit our website for more information!</p>
|
80
|
+
<a href="https://example.com" class="button">Visit Our Website</a>
|
81
|
+
</div>
|
82
|
+
<div class="email-footer">
|
83
|
+
<p>© 2024 Company Name. All rights reserved.</p>
|
84
|
+
<p>If you no longer wish to receive these emails, please <a href="#">unsubscribe</a>.</p>
|
85
|
+
</div>
|
86
|
+
</div>
|
87
|
+
</body>
|
88
|
+
</html>
|
py2ls/ips.py
CHANGED
@@ -47,6 +47,8 @@ from fuzzywuzzy import fuzz, process
|
|
47
47
|
from langdetect import detect
|
48
48
|
from duckduckgo_search import DDGS
|
49
49
|
|
50
|
+
from bs4 import BeautifulSoup
|
51
|
+
|
50
52
|
from . import netfinder
|
51
53
|
|
52
54
|
# from .plot import get_color
|
@@ -58,16 +60,14 @@ except NameError:
|
|
58
60
|
pass
|
59
61
|
|
60
62
|
|
61
|
-
|
62
|
-
|
63
63
|
# set 'dir_save'
|
64
|
-
if
|
64
|
+
if "dar" in sys.platform:
|
65
65
|
dir_save = "/Users/macjianfeng/Dropbox/Downloads/"
|
66
66
|
else:
|
67
|
-
if
|
68
|
-
dir_save= "Z:\\Jianfeng\\temp\\"
|
69
|
-
elif
|
70
|
-
dir_data="/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
|
67
|
+
if "win" in sys.platform:
|
68
|
+
dir_save = "Z:\\Jianfeng\\temp\\"
|
69
|
+
elif "lin" in sys.platform:
|
70
|
+
dir_data = "/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
|
71
71
|
|
72
72
|
|
73
73
|
def unique(lst, ascending=None):
|
@@ -272,6 +272,7 @@ def upgrade(module="py2ls"):
|
|
272
272
|
except subprocess.CalledProcessError as e:
|
273
273
|
print(f"An error occurred while upgrading py2ls: {e}")
|
274
274
|
|
275
|
+
|
275
276
|
def get_version(pkg):
|
276
277
|
import importlib.metadata
|
277
278
|
|
@@ -791,7 +792,7 @@ def str2html(text_list, strict=False):
|
|
791
792
|
|
792
793
|
return html_content
|
793
794
|
|
794
|
-
|
795
|
+
|
795
796
|
def sreplace(*args, **kwargs):
|
796
797
|
"""
|
797
798
|
sreplace(text, by=None, robust=True)
|
@@ -1812,6 +1813,7 @@ def sort_kind(df, by="name", ascending=True):
|
|
1812
1813
|
sorted_df = df.iloc[sorted_index].reset_index(drop=True)
|
1813
1814
|
return sorted_df
|
1814
1815
|
|
1816
|
+
|
1815
1817
|
def isa(content, kind):
|
1816
1818
|
"""
|
1817
1819
|
content, kind='img'
|
@@ -1840,6 +1842,8 @@ def isa(content, kind):
|
|
1840
1842
|
elif "color" in kind.lower(): # file
|
1841
1843
|
return is_str_color(content)
|
1842
1844
|
elif "html" in kind.lower():
|
1845
|
+
if content is None or not isinstance(content, str):
|
1846
|
+
return False
|
1843
1847
|
# Remove leading and trailing whitespace
|
1844
1848
|
content = content.strip()
|
1845
1849
|
# Check for common HTML tags using regex
|
@@ -1858,7 +1862,10 @@ def isa(content, kind):
|
|
1858
1862
|
print(f"{kind} was not set up correctly")
|
1859
1863
|
return False
|
1860
1864
|
|
1865
|
+
|
1861
1866
|
import sys
|
1867
|
+
|
1868
|
+
|
1862
1869
|
def get_os():
|
1863
1870
|
os_name = sys.platform
|
1864
1871
|
if "dar" in os_name:
|
@@ -3992,11 +3999,12 @@ format_excel(
|
|
3992
3999
|
|
3993
4000
|
from IPython.display import display, HTML, Markdown, Image
|
3994
4001
|
|
4002
|
+
|
3995
4003
|
def preview(var):
|
3996
4004
|
"""Master function to preview formatted variables in Jupyter."""
|
3997
4005
|
|
3998
4006
|
if isinstance(var, str):
|
3999
|
-
if isa(var,
|
4007
|
+
if isa(var, "html"):
|
4000
4008
|
display(HTML(var)) # Render as HTML
|
4001
4009
|
# Check if it's a valid markdown
|
4002
4010
|
elif var.startswith("#"):
|
@@ -4004,7 +4012,8 @@ def preview(var):
|
|
4004
4012
|
else:
|
4005
4013
|
# Otherwise, display as plain text
|
4006
4014
|
print(var)
|
4007
|
-
|
4015
|
+
elif isinstance(var, BeautifulSoup):
|
4016
|
+
preview(str(var))
|
4008
4017
|
elif isinstance(var, pd.DataFrame):
|
4009
4018
|
# Display pandas DataFrame
|
4010
4019
|
display(var)
|
@@ -4036,4 +4045,4 @@ def preview(var):
|
|
4036
4045
|
# preview("This is a plain text message.")
|
4037
4046
|
# preview("# This is a Markdown header")
|
4038
4047
|
# preview(pd.DataFrame({"Name": ["Alice", "Bob"], "Age": [25, 30]}))
|
4039
|
-
# preview({"key": "value", "numbers": [1, 2, 3]})
|
4048
|
+
# preview({"key": "value", "numbers": [1, 2, 3]})
|
@@ -1,11 +1,16 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: py2ls
|
3
|
-
Version: 0.1.10.
|
3
|
+
Version: 0.1.10.26
|
4
4
|
Summary: py(thon)2(too)ls
|
5
5
|
Author: Jianfeng
|
6
6
|
Author-email: Jianfeng.Liu0413@gmail.com
|
7
|
-
Requires-Python: >=3.
|
7
|
+
Requires-Python: >=3.5,<4.0
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Programming Language :: Python :: 3.5
|
10
|
+
Classifier: Programming Language :: Python :: 3.6
|
11
|
+
Classifier: Programming Language :: Python :: 3.7
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
9
14
|
Classifier: Programming Language :: Python :: 3.10
|
10
15
|
Classifier: Programming Language :: Python :: 3.11
|
11
16
|
Classifier: Programming Language :: Python :: 3.12
|
@@ -172,13 +172,14 @@ py2ls/.gitignore,sha256=y7GvbD_zZkjPVVIue8AyiuFkDMuUbvMaV65Lgu89To8,2763
|
|
172
172
|
py2ls/LICENSE,sha256=UOZ1F5fFDe3XXvG4oNnkL1-Ecun7zpHzRxjp-XsMeAo,11324
|
173
173
|
py2ls/README.md,sha256=CwvJWAnSXnCnrVHlnEbrxxi6MbjbE_MT6DH2D53S818,11572
|
174
174
|
py2ls/__init__.py,sha256=Nn8jTIvySX7t7DMJ8VNRVctTStgXGjHldOIdZ35PdW8,165
|
175
|
-
py2ls/batman.py,sha256=
|
175
|
+
py2ls/batman.py,sha256=E7gYofbDzN7S5oCmO_dd5Z1bxxhoYMJSD6s-VaF388E,11398
|
176
176
|
py2ls/brain_atlas.py,sha256=w1o5EelRjq89zuFJUNSz4Da8HnTCwAwDAZ4NU4a-bAY,5486
|
177
177
|
py2ls/chat.py,sha256=Yr22GoIvoWhpV3m4fdwV_I0Mn77La346_ymSinR-ORA,3793
|
178
178
|
py2ls/correlators.py,sha256=RbOaJIPLCHJtUm5SFi_4dCJ7VFUPWR0PErfK3K26ad4,18243
|
179
179
|
py2ls/data/.DS_Store,sha256=iH2O541jT_5mlTPavY_d5V2prS9zhNx4Pv7yhmbwaHI,6148
|
180
180
|
py2ls/data/db2ls_sql_chtsht.json,sha256=ls9d7Sm8TLeujanWHfHlWhU85Qz1KnAizO_9X3wUH7E,6933
|
181
181
|
py2ls/data/docs_links.json,sha256=kXgbbWo0b8bfV4n6iuuUNLnZipIyLzokUO6Lzmf7nO4,101829
|
182
|
+
py2ls/data/email/email_html_template.html,sha256=UIg3aixWfdNsvVx-j2dX1M5N3G-6DgrnV1Ya1cLjiUQ,2809
|
182
183
|
py2ls/data/lang_code_iso639.json,sha256=qZiU7H2RLJjDMXK22C-jhwzLJCI5vKmampjB1ys4ek4,2157
|
183
184
|
py2ls/data/styles/example/style1.pdf,sha256=Pt_qQJ5kiCSIPiz3TWSwEffHUdj75kKXnZ4MPqpEx4I,29873
|
184
185
|
py2ls/data/styles/example/style2.pdf,sha256=0xduPLPulET38LEP2V2H_q70wqlrrBEo8ttqO-FMrfQ,25449
|
@@ -206,7 +207,7 @@ py2ls/doc.py,sha256=xN3g1OWfoaGUhikbJ0NqbN5eKy1VZVvWwRlhHMgyVEc,4243
|
|
206
207
|
py2ls/export_requirements.py,sha256=x2WgUF0jYKz9GfA1MVKN-MdsM-oQ8yUeC6Ua8oCymio,2325
|
207
208
|
py2ls/freqanalysis.py,sha256=F4218VSPbgL5tnngh6xNCYuNnfR-F_QjECUUxrPYZss,32594
|
208
209
|
py2ls/ich2ls.py,sha256=3E9R8oVpyYZXH5PiIQgT3CN5NxLe4Dwtm2LwaeacE6I,21381
|
209
|
-
py2ls/ips.py,sha256=
|
210
|
+
py2ls/ips.py,sha256=nhQBp5J2QjodplBXMCVBW-7w7rVzG6IalqCuaJgf7Xc,147799
|
210
211
|
py2ls/netfinder.py,sha256=vgOOMhzwbjRuLWMAPyf_kh3HoOhsJ9dlA-tCkMf7kNU,55371
|
211
212
|
py2ls/ocr.py,sha256=5lhUbJufIKRSOL6wAWVLEo8TqMYSjoI_Q-IO-_4u3DE,31419
|
212
213
|
py2ls/plot.py,sha256=yj-AfnYNr1ha_Y5EimTsUVSooFc36nE0KCQ8cP9_Trs,97601
|
@@ -215,6 +216,6 @@ py2ls/sleep_events_detectors.py,sha256=bQA3HJqv5qnYKJJEIhCyhlDtkXQfIzqksnD0YRXso
|
|
215
216
|
py2ls/stats.py,sha256=fJmXQ9Lq460StOn-kfEljE97cySq7876HUPTnpB5hLs,38123
|
216
217
|
py2ls/translator.py,sha256=zBeq4pYZeroqw3DT-5g7uHfVqKd-EQptT6LJ-Adi8JY,34244
|
217
218
|
py2ls/wb_detector.py,sha256=7y6TmBUj9exCZeIgBAJ_9hwuhkDh1x_-yg4dvNY1_GQ,6284
|
218
|
-
py2ls-0.1.10.
|
219
|
-
py2ls-0.1.10.
|
220
|
-
py2ls-0.1.10.
|
219
|
+
py2ls-0.1.10.26.dist-info/METADATA,sha256=jwKMA56onOk6i2BpDchh8dsg8HTzNDbODlNNLu61On4,20040
|
220
|
+
py2ls-0.1.10.26.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
221
|
+
py2ls-0.1.10.26.dist-info/RECORD,,
|
File without changes
|