markdown-merge 0.1.0__tar.gz → 0.1.5__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.
Potentially problematic release.
This version of markdown-merge might be problematic. Click here for more details.
- markdown_merge-0.1.5/MANIFEST.in +5 -0
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/PKG-INFO +7 -13
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/README.md +0 -5
- markdown_merge-0.1.5/markdown_merge/__init__.py +2 -0
- markdown_merge-0.1.5/markdown_merge/_modidx.py +19 -0
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge/core.py +29 -23
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/PKG-INFO +7 -13
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/SOURCES.txt +2 -2
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/requires.txt +1 -1
- markdown_merge-0.1.5/pyproject.toml +3 -0
- markdown_merge-0.1.5/settings.ini +39 -0
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/setup.py +22 -21
- markdown_merge-0.1.0/markdown_merge/__init__.py +0 -2
- markdown_merge-0.1.0/markdown_merge/_modidx.py +0 -37
- markdown_merge-0.1.0/markdown_merge/_nbdev.py +0 -9
- markdown_merge-0.1.0/markdown_merge/markdown_merge.py +0 -85
- markdown_merge-0.1.0/pyproject.toml +0 -11
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/LICENSE +0 -0
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/dependency_links.txt +0 -0
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/entry_points.txt +0 -0
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/not-zip-safe +0 -0
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/top_level.txt +0 -0
- {markdown_merge-0.1.0 → markdown_merge-0.1.5}/setup.cfg +0 -0
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: markdown_merge
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Send
|
|
5
|
-
Home-page: https://github.com/
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Send email using markdown
|
|
5
|
+
Home-page: https://github.com/AnswerDotAI/markdown_merge
|
|
6
6
|
Author: Jeremy Howard
|
|
7
|
-
Author-email:
|
|
7
|
+
Author-email: github@jhoward.fastmail.fm
|
|
8
8
|
License: Apache Software License 2.0
|
|
9
9
|
Keywords: email mail markdown template
|
|
10
|
-
Classifier: Development Status ::
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
12
|
Classifier: Natural Language :: English
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Classifier:
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
18
17
|
Requires-Python: >=3.10
|
|
19
18
|
Description-Content-Type: text/markdown
|
|
20
19
|
License-File: LICENSE
|
|
21
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: fastcore
|
|
22
21
|
Requires-Dist: markdown
|
|
23
22
|
Provides-Extra: dev
|
|
24
23
|
Dynamic: author
|
|
@@ -40,11 +39,6 @@ Dynamic: summary
|
|
|
40
39
|
|
|
41
40
|
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
42
41
|
|
|
43
|
-
``` python
|
|
44
|
-
#hide
|
|
45
|
-
from markdown_merge.markdown_merge import *
|
|
46
|
-
```
|
|
47
|
-
|
|
48
42
|
## Install
|
|
49
43
|
|
|
50
44
|
`pip install markdown_merge`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Autogenerated by nbdev
|
|
2
|
+
|
|
3
|
+
d = { 'settings': { 'branch': 'master',
|
|
4
|
+
'doc_baseurl': '/markdown_merge',
|
|
5
|
+
'doc_host': 'https://AnswerDotAI.github.io',
|
|
6
|
+
'git_url': 'https://github.com/AnswerDotAI/markdown_merge',
|
|
7
|
+
'lib_path': 'markdown_merge'},
|
|
8
|
+
'syms': { 'markdown_merge.core': { 'markdown_merge.core.MarkdownMerge': ('core.html#markdownmerge', 'markdown_merge/core.py'),
|
|
9
|
+
'markdown_merge.core.MarkdownMerge.__init__': ( 'core.html#markdownmerge.__init__',
|
|
10
|
+
'markdown_merge/core.py'),
|
|
11
|
+
'markdown_merge.core.MarkdownMerge.reset': ('core.html#markdownmerge.reset', 'markdown_merge/core.py'),
|
|
12
|
+
'markdown_merge.core.MarkdownMerge.send_msgs': ( 'core.html#markdownmerge.send_msgs',
|
|
13
|
+
'markdown_merge/core.py'),
|
|
14
|
+
'markdown_merge.core.attach_file': ('core.html#attach_file', 'markdown_merge/core.py'),
|
|
15
|
+
'markdown_merge.core.create_multipart_msg': ( 'core.html#create_multipart_msg',
|
|
16
|
+
'markdown_merge/core.py'),
|
|
17
|
+
'markdown_merge.core.get_addr': ('core.html#get_addr', 'markdown_merge/core.py'),
|
|
18
|
+
'markdown_merge.core.md2email': ('core.html#md2email', 'markdown_merge/core.py'),
|
|
19
|
+
'markdown_merge.core.smtp_connection': ('core.html#smtp_connection', 'markdown_merge/core.py')}}}
|
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
"""API details"""
|
|
2
2
|
|
|
3
|
-
# AUTOGENERATED! DO NOT EDIT! File to edit: ../00_core.ipynb.
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
|
|
4
4
|
|
|
5
5
|
# %% auto 0
|
|
6
6
|
__all__ = ['get_addr', 'attach_file', 'create_multipart_msg', 'md2email', 'smtp_connection', 'MarkdownMerge']
|
|
7
7
|
|
|
8
|
-
# %% ../00_core.ipynb
|
|
8
|
+
# %% ../nbs/00_core.ipynb
|
|
9
9
|
import os,mimetypes,smtplib
|
|
10
10
|
|
|
11
11
|
from fastcore.utils import *
|
|
12
12
|
from email.mime.multipart import MIMEMultipart
|
|
13
13
|
from email.mime.text import MIMEText
|
|
14
14
|
from email.mime.base import MIMEBase
|
|
15
|
+
from email.policy import EmailPolicy
|
|
15
16
|
from email import encoders
|
|
16
17
|
|
|
17
18
|
from contextlib import contextmanager
|
|
18
19
|
from markdown import markdown
|
|
19
20
|
from email.headerregistry import Address
|
|
21
|
+
from email.header import Header
|
|
20
22
|
from time import sleep
|
|
21
23
|
|
|
22
|
-
# %% ../00_core.ipynb
|
|
24
|
+
# %% ../nbs/00_core.ipynb
|
|
23
25
|
def get_addr(email, name=None):
|
|
24
26
|
"Convert `email` and optional `name` into an email `Address` object"
|
|
25
27
|
return Address(email if name is None else name, addr_spec=email)
|
|
26
28
|
|
|
27
|
-
# %% ../00_core.ipynb
|
|
29
|
+
# %% ../nbs/00_core.ipynb
|
|
28
30
|
def attach_file(msg, f):
|
|
29
31
|
"Attach file `f` to message `msg`"
|
|
30
32
|
mtype,_ = mimetypes.guess_type(f)
|
|
@@ -35,10 +37,10 @@ def attach_file(msg, f):
|
|
|
35
37
|
part['Content-Disposition'] = f'attachment; filename={Path(f).name}'
|
|
36
38
|
msg.attach(part)
|
|
37
39
|
|
|
38
|
-
# %% ../00_core.ipynb
|
|
40
|
+
# %% ../nbs/00_core.ipynb
|
|
39
41
|
def create_multipart_msg(subj, from_addr, to_addrs, md=None, html=None, attach=None):
|
|
40
42
|
"Create a multipart email with markdown text and HTML"
|
|
41
|
-
msg = MIMEMultipart('alternative')
|
|
43
|
+
msg = MIMEMultipart('alternative', policy=EmailPolicy())
|
|
42
44
|
msg['Subject'],msg['From'] = subj,str(from_addr)
|
|
43
45
|
msg['To'] = ', '.join([str(a) for a in listify(to_addrs)])
|
|
44
46
|
if md: msg.attach(MIMEText(md, 'plain'))
|
|
@@ -46,23 +48,21 @@ def create_multipart_msg(subj, from_addr, to_addrs, md=None, html=None, attach=N
|
|
|
46
48
|
for f in listify(attach): attach_file(msg, f)
|
|
47
49
|
return msg
|
|
48
50
|
|
|
49
|
-
# %% ../00_core.ipynb
|
|
51
|
+
# %% ../nbs/00_core.ipynb
|
|
50
52
|
def md2email(subj, from_addr, to_addrs, md, attach=None):
|
|
51
53
|
"Create a multipart email from markdown"
|
|
52
54
|
html = markdown(md)
|
|
53
55
|
return create_multipart_msg(subj, from_addr, to_addrs, md=md, html=html, attach=attach)
|
|
54
56
|
|
|
55
|
-
# %% ../00_core.ipynb
|
|
56
|
-
@contextmanager
|
|
57
|
+
# %% ../nbs/00_core.ipynb
|
|
57
58
|
def smtp_connection(host, port, user=None, password=None, use_ssl=True, use_tls=False):
|
|
58
|
-
"
|
|
59
|
+
"Create and return an SMTP connection"
|
|
59
60
|
conn = smtplib.SMTP_SSL(host, port) if use_ssl else smtplib.SMTP(host, port)
|
|
60
61
|
if use_tls and not use_ssl: conn.starttls()
|
|
61
62
|
if user and password: conn.login(user, password)
|
|
62
|
-
|
|
63
|
-
finally: conn.quit()
|
|
63
|
+
return conn
|
|
64
64
|
|
|
65
|
-
# %% ../00_core.ipynb
|
|
65
|
+
# %% ../nbs/00_core.ipynb
|
|
66
66
|
class MarkdownMerge:
|
|
67
67
|
"Send templated email merge messages formatted with Markdown"
|
|
68
68
|
def __init__(self, addrs, from_addr, subj, msg, smtp_cfg=None, inserts=None, test=False):
|
|
@@ -70,16 +70,22 @@ class MarkdownMerge:
|
|
|
70
70
|
self.inserts = [{}]*len(addrs) if inserts is None else inserts
|
|
71
71
|
self.smtp_cfg,self.test = smtp_cfg,test
|
|
72
72
|
|
|
73
|
-
def send_msgs(self, pause=0.
|
|
73
|
+
def send_msgs(self, pause=0.2):
|
|
74
74
|
"Send all unsent messages to `addrs` with `pause` secs between each send"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
conn = smtp_connection(**self.smtp_cfg)
|
|
76
|
+
while self.i < len(self.addrs):
|
|
77
|
+
addr,insert = self.addrs[self.i],self.inserts[self.i]
|
|
78
|
+
msg = self.msg.format(**insert)
|
|
79
|
+
eml = md2email(self.subj, self.from_addr, addr, md=msg)
|
|
80
|
+
if self.test: print(f"To: {addr}\n{'-'*40}\n{msg}\n{'='*40}\n")
|
|
81
|
+
else:
|
|
82
|
+
conn.send_message(eml)
|
|
83
|
+
sleep(pause)
|
|
84
|
+
self.i += 1
|
|
85
|
+
if self.i%100==0:
|
|
86
|
+
print(self.i)
|
|
87
|
+
conn.quit()
|
|
88
|
+
conn = smtp_connection(**self.smtp_cfg)
|
|
89
|
+
conn.quit()
|
|
84
90
|
|
|
85
91
|
def reset(self): self.i=0
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: markdown_merge
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Send
|
|
5
|
-
Home-page: https://github.com/
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Send email using markdown
|
|
5
|
+
Home-page: https://github.com/AnswerDotAI/markdown_merge
|
|
6
6
|
Author: Jeremy Howard
|
|
7
|
-
Author-email:
|
|
7
|
+
Author-email: github@jhoward.fastmail.fm
|
|
8
8
|
License: Apache Software License 2.0
|
|
9
9
|
Keywords: email mail markdown template
|
|
10
|
-
Classifier: Development Status ::
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
12
|
Classifier: Natural Language :: English
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Classifier:
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
18
17
|
Requires-Python: >=3.10
|
|
19
18
|
Description-Content-Type: text/markdown
|
|
20
19
|
License-File: LICENSE
|
|
21
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: fastcore
|
|
22
21
|
Requires-Dist: markdown
|
|
23
22
|
Provides-Extra: dev
|
|
24
23
|
Dynamic: author
|
|
@@ -40,11 +39,6 @@ Dynamic: summary
|
|
|
40
39
|
|
|
41
40
|
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
42
41
|
|
|
43
|
-
``` python
|
|
44
|
-
#hide
|
|
45
|
-
from markdown_merge.markdown_merge import *
|
|
46
|
-
```
|
|
47
|
-
|
|
48
42
|
## Install
|
|
49
43
|
|
|
50
44
|
`pip install markdown_merge`
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
2
3
|
README.md
|
|
3
4
|
pyproject.toml
|
|
5
|
+
settings.ini
|
|
4
6
|
setup.py
|
|
5
7
|
markdown_merge/__init__.py
|
|
6
8
|
markdown_merge/_modidx.py
|
|
7
|
-
markdown_merge/_nbdev.py
|
|
8
9
|
markdown_merge/core.py
|
|
9
|
-
markdown_merge/markdown_merge.py
|
|
10
10
|
markdown_merge.egg-info/PKG-INFO
|
|
11
11
|
markdown_merge.egg-info/SOURCES.txt
|
|
12
12
|
markdown_merge.egg-info/dependency_links.txt
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[DEFAULT]
|
|
2
|
+
repo = markdown_merge
|
|
3
|
+
lib_name = markdown_merge
|
|
4
|
+
version = 0.1.5
|
|
5
|
+
min_python = 3.10
|
|
6
|
+
license = apache2
|
|
7
|
+
black_formatting = False
|
|
8
|
+
requirements = fastcore markdown
|
|
9
|
+
doc_path = _docs
|
|
10
|
+
lib_path = markdown_merge
|
|
11
|
+
nbs_path = nbs
|
|
12
|
+
recursive = True
|
|
13
|
+
tst_flags = notest
|
|
14
|
+
put_version_in_init = True
|
|
15
|
+
update_pyproject = True
|
|
16
|
+
branch = master
|
|
17
|
+
custom_sidebar = False
|
|
18
|
+
doc_host = https://AnswerDotAI.github.io
|
|
19
|
+
doc_baseurl = /markdown_merge
|
|
20
|
+
git_url = https://github.com/AnswerDotAI/markdown_merge
|
|
21
|
+
title = markdown_merge
|
|
22
|
+
audience = Developers
|
|
23
|
+
author = Jeremy Howard
|
|
24
|
+
author_email = github@jhoward.fastmail.fm
|
|
25
|
+
copyright = 2025 onwards, Jeremy Howard
|
|
26
|
+
description = Send email using markdown
|
|
27
|
+
keywords = email mail markdown template
|
|
28
|
+
language = English
|
|
29
|
+
status = 3
|
|
30
|
+
user = AnswerDotAI
|
|
31
|
+
cell_number = False
|
|
32
|
+
readme_nb = index.ipynb
|
|
33
|
+
allowed_metadata_keys =
|
|
34
|
+
allowed_cell_metadata_keys =
|
|
35
|
+
jupyter_hooks = False
|
|
36
|
+
clean_ids = True
|
|
37
|
+
clear_all = False
|
|
38
|
+
skip_procs =
|
|
39
|
+
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from pkg_resources import parse_version
|
|
2
2
|
from configparser import ConfigParser
|
|
3
|
-
import setuptools,
|
|
3
|
+
import setuptools, shlex
|
|
4
4
|
assert parse_version(setuptools.__version__)>=parse_version('36.2')
|
|
5
5
|
|
|
6
6
|
# note: all settings are in settings.ini; edit there, not here
|
|
7
7
|
config = ConfigParser(delimiters=['='])
|
|
8
|
-
config.read('settings.ini')
|
|
8
|
+
config.read('settings.ini', encoding='utf-8')
|
|
9
9
|
cfg = config['DEFAULT']
|
|
10
10
|
|
|
11
11
|
cfg_keys = 'version description keywords author author_email'.split()
|
|
@@ -13,46 +13,46 @@ expected = cfg_keys + "lib_name user branch license status min_python audience l
|
|
|
13
13
|
for o in expected: assert o in cfg, "missing expected setting: {}".format(o)
|
|
14
14
|
setup_cfg = {o:cfg[o] for o in cfg_keys}
|
|
15
15
|
|
|
16
|
-
if len(sys.argv)>1 and sys.argv[1]=='version':
|
|
17
|
-
print(setup_cfg['version'])
|
|
18
|
-
exit()
|
|
19
|
-
|
|
20
16
|
licenses = {
|
|
21
17
|
'apache2': ('Apache Software License 2.0','OSI Approved :: Apache Software License'),
|
|
18
|
+
'mit': ('MIT License', 'OSI Approved :: MIT License'),
|
|
19
|
+
'gpl2': ('GNU General Public License v2', 'OSI Approved :: GNU General Public License v2 (GPLv2)'),
|
|
20
|
+
'gpl3': ('GNU General Public License v3', 'OSI Approved :: GNU General Public License v3 (GPLv3)'),
|
|
21
|
+
'bsd3': ('BSD License', 'OSI Approved :: BSD License'),
|
|
22
22
|
}
|
|
23
23
|
statuses = [ '1 - Planning', '2 - Pre-Alpha', '3 - Alpha',
|
|
24
24
|
'4 - Beta', '5 - Production/Stable', '6 - Mature', '7 - Inactive' ]
|
|
25
|
-
py_versions = '3.
|
|
26
|
-
min_python = cfg['min_python']
|
|
27
|
-
lic = licenses[cfg['license']]
|
|
25
|
+
py_versions = '3.6 3.7 3.8 3.9 3.10 3.11 3.12'.split()
|
|
28
26
|
|
|
29
|
-
requirements =
|
|
30
|
-
if cfg.get('
|
|
31
|
-
|
|
27
|
+
requirements = shlex.split(cfg.get('requirements', ''))
|
|
28
|
+
if cfg.get('pip_requirements'): requirements += shlex.split(cfg.get('pip_requirements', ''))
|
|
29
|
+
min_python = cfg['min_python']
|
|
30
|
+
lic = licenses.get(cfg['license'].lower(), (cfg['license'], None))
|
|
32
31
|
dev_requirements = (cfg.get('dev_requirements') or '').split()
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
package_data = dict()
|
|
34
|
+
pkg_data = cfg.get('package_data', None)
|
|
35
|
+
if pkg_data:
|
|
36
|
+
package_data[cfg['lib_name']] = pkg_data.split() # split as multiple files might be listed
|
|
37
|
+
# Add package data to setup_cfg for setuptools.setup(..., **setup_cfg)
|
|
38
|
+
setup_cfg['package_data'] = package_data
|
|
39
39
|
|
|
40
40
|
setuptools.setup(
|
|
41
|
-
name = '
|
|
41
|
+
name = cfg['lib_name'],
|
|
42
42
|
license = lic[0],
|
|
43
43
|
classifiers = [
|
|
44
44
|
'Development Status :: ' + statuses[int(cfg['status'])],
|
|
45
45
|
'Intended Audience :: ' + cfg['audience'].title(),
|
|
46
|
-
'License :: ' + lic[1],
|
|
47
46
|
'Natural Language :: ' + cfg['language'].title(),
|
|
48
|
-
] + ['Programming Language :: Python :: '+o for o in py_versions[py_versions.index(min_python):]],
|
|
47
|
+
] + ['Programming Language :: Python :: '+o for o in py_versions[py_versions.index(min_python):]] + (['License :: ' + lic[1] ] if lic[1] else []),
|
|
49
48
|
url = cfg['git_url'],
|
|
50
49
|
packages = setuptools.find_packages(),
|
|
51
50
|
include_package_data = True,
|
|
52
51
|
install_requires = requirements,
|
|
53
52
|
extras_require={ 'dev': dev_requirements },
|
|
53
|
+
dependency_links = cfg.get('dep_links','').split(),
|
|
54
54
|
python_requires = '>=' + cfg['min_python'],
|
|
55
|
-
long_description =
|
|
55
|
+
long_description = open('README.md', encoding='utf-8').read(),
|
|
56
56
|
long_description_content_type = 'text/markdown',
|
|
57
57
|
zip_safe = False,
|
|
58
58
|
entry_points = {
|
|
@@ -61,3 +61,4 @@ setuptools.setup(
|
|
|
61
61
|
},
|
|
62
62
|
**setup_cfg)
|
|
63
63
|
|
|
64
|
+
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Autogenerated by nbdev
|
|
2
|
-
|
|
3
|
-
d = { 'settings': { 'branch': 'master',
|
|
4
|
-
'doc_baseurl': '/',
|
|
5
|
-
'doc_host': 'https://answerdotai.github.io',
|
|
6
|
-
'git_url': 'https://github.com/answerdotai/markdown_merge/tree/master/',
|
|
7
|
-
'lib_path': 'markdown_merge'},
|
|
8
|
-
'syms': { 'markdown_merge.core': { 'markdown_merge.core.MarkdownMerge': ('core.html#markdownmerge', 'markdown_merge/core.py'),
|
|
9
|
-
'markdown_merge.core.MarkdownMerge.__init__': ( 'core.html#markdownmerge.__init__',
|
|
10
|
-
'markdown_merge/core.py'),
|
|
11
|
-
'markdown_merge.core.MarkdownMerge.reset': ('core.html#markdownmerge.reset', 'markdown_merge/core.py'),
|
|
12
|
-
'markdown_merge.core.MarkdownMerge.send_msgs': ( 'core.html#markdownmerge.send_msgs',
|
|
13
|
-
'markdown_merge/core.py'),
|
|
14
|
-
'markdown_merge.core.attach_file': ('core.html#attach_file', 'markdown_merge/core.py'),
|
|
15
|
-
'markdown_merge.core.create_multipart_msg': ( 'core.html#create_multipart_msg',
|
|
16
|
-
'markdown_merge/core.py'),
|
|
17
|
-
'markdown_merge.core.get_addr': ('core.html#get_addr', 'markdown_merge/core.py'),
|
|
18
|
-
'markdown_merge.core.md2email': ('core.html#md2email', 'markdown_merge/core.py'),
|
|
19
|
-
'markdown_merge.core.smtp_connection': ('core.html#smtp_connection', 'markdown_merge/core.py')},
|
|
20
|
-
'markdown_merge.markdown_merge': { 'markdown_merge.markdown_merge.MarkdownMerge': ( 'markdown_merge.html#markdownmerge',
|
|
21
|
-
'markdown_merge/markdown_merge.py'),
|
|
22
|
-
'markdown_merge.markdown_merge.MarkdownMerge.__init__': ( 'markdown_merge.html#markdownmerge.__init__',
|
|
23
|
-
'markdown_merge/markdown_merge.py'),
|
|
24
|
-
'markdown_merge.markdown_merge.MarkdownMerge.reset': ( 'markdown_merge.html#markdownmerge.reset',
|
|
25
|
-
'markdown_merge/markdown_merge.py'),
|
|
26
|
-
'markdown_merge.markdown_merge.MarkdownMerge.send_msgs': ( 'markdown_merge.html#markdownmerge.send_msgs',
|
|
27
|
-
'markdown_merge/markdown_merge.py'),
|
|
28
|
-
'markdown_merge.markdown_merge.attach_file': ( 'markdown_merge.html#attach_file',
|
|
29
|
-
'markdown_merge/markdown_merge.py'),
|
|
30
|
-
'markdown_merge.markdown_merge.create_multipart_msg': ( 'markdown_merge.html#create_multipart_msg',
|
|
31
|
-
'markdown_merge/markdown_merge.py'),
|
|
32
|
-
'markdown_merge.markdown_merge.get_addr': ( 'markdown_merge.html#get_addr',
|
|
33
|
-
'markdown_merge/markdown_merge.py'),
|
|
34
|
-
'markdown_merge.markdown_merge.md2email': ( 'markdown_merge.html#md2email',
|
|
35
|
-
'markdown_merge/markdown_merge.py'),
|
|
36
|
-
'markdown_merge.markdown_merge.smtp_connection': ( 'markdown_merge.html#smtp_connection',
|
|
37
|
-
'markdown_merge/markdown_merge.py')}}}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"""API details"""
|
|
2
|
-
|
|
3
|
-
# AUTOGENERATED! DO NOT EDIT! File to edit: ../00_markdown_merge.ipynb.
|
|
4
|
-
|
|
5
|
-
# %% auto 0
|
|
6
|
-
__all__ = ['get_addr', 'attach_file', 'create_multipart_msg', 'md2email', 'smtp_connection', 'MarkdownMerge']
|
|
7
|
-
|
|
8
|
-
# %% ../00_markdown_merge.ipynb 2
|
|
9
|
-
import os,mimetypes,smtplib
|
|
10
|
-
|
|
11
|
-
from fastcore.utils import *
|
|
12
|
-
from email.mime.multipart import MIMEMultipart
|
|
13
|
-
from email.mime.text import MIMEText
|
|
14
|
-
from email.mime.base import MIMEBase
|
|
15
|
-
from email import encoders
|
|
16
|
-
|
|
17
|
-
from contextlib import contextmanager
|
|
18
|
-
from markdown import markdown
|
|
19
|
-
from email.headerregistry import Address
|
|
20
|
-
from time import sleep
|
|
21
|
-
|
|
22
|
-
# %% ../00_markdown_merge.ipynb 6
|
|
23
|
-
def get_addr(email, name=None):
|
|
24
|
-
"Convert `email` and optional `name` into an email `Address` object"
|
|
25
|
-
return Address(email if name is None else name, addr_spec=email)
|
|
26
|
-
|
|
27
|
-
# %% ../00_markdown_merge.ipynb 9
|
|
28
|
-
def attach_file(msg, f):
|
|
29
|
-
"Attach file `f` to message `msg`"
|
|
30
|
-
mtype,_ = mimetypes.guess_type(f)
|
|
31
|
-
main,sub = (mtype or 'application/octet-stream').split('/', 1)
|
|
32
|
-
part = MIMEBase(main, sub)
|
|
33
|
-
with open(f, 'rb') as fp: part.set_payload(fp.read())
|
|
34
|
-
encoders.encode_base64(part)
|
|
35
|
-
part['Content-Disposition'] = f'attachment; filename={Path(f).name}'
|
|
36
|
-
msg.attach(part)
|
|
37
|
-
|
|
38
|
-
# %% ../00_markdown_merge.ipynb 11
|
|
39
|
-
def create_multipart_msg(subj, from_addr, to_addrs, md=None, html=None, attach=None):
|
|
40
|
-
"Create a multipart email with markdown text and HTML"
|
|
41
|
-
msg = MIMEMultipart('alternative')
|
|
42
|
-
msg['Subject'],msg['From'] = subj,str(from_addr)
|
|
43
|
-
msg['To'] = ', '.join([str(a) for a in listify(to_addrs)])
|
|
44
|
-
if md: msg.attach(MIMEText(md, 'plain'))
|
|
45
|
-
if html: msg.attach(MIMEText(html, 'html'))
|
|
46
|
-
for f in listify(attach): attach_file(msg, f)
|
|
47
|
-
return msg
|
|
48
|
-
|
|
49
|
-
# %% ../00_markdown_merge.ipynb 13
|
|
50
|
-
def md2email(subj, from_addr, to_addrs, md, attach=None):
|
|
51
|
-
"Create a multipart email from markdown"
|
|
52
|
-
html = markdown(md)
|
|
53
|
-
return create_multipart_msg(subj, from_addr, to_addrs, md=md, html=html, attach=attach)
|
|
54
|
-
|
|
55
|
-
# %% ../00_markdown_merge.ipynb 22
|
|
56
|
-
@contextmanager
|
|
57
|
-
def smtp_connection(host, port, user=None, password=None, use_ssl=True, use_tls=False):
|
|
58
|
-
"Context manager for SMTP connection"
|
|
59
|
-
conn = smtplib.SMTP_SSL(host, port) if use_ssl else smtplib.SMTP(host, port)
|
|
60
|
-
if use_tls and not use_ssl: conn.starttls()
|
|
61
|
-
if user and password: conn.login(user, password)
|
|
62
|
-
try: yield conn
|
|
63
|
-
finally: conn.quit()
|
|
64
|
-
|
|
65
|
-
# %% ../00_markdown_merge.ipynb 26
|
|
66
|
-
class MarkdownMerge:
|
|
67
|
-
"Send templated email merge messages formatted with Markdown"
|
|
68
|
-
def __init__(self, addrs, from_addr, subj, msg, smtp_cfg=None, inserts=None, test=False):
|
|
69
|
-
self.addrs,self.from_addr,self.subj,self.msg,self.i = addrs,from_addr,subj,msg,0
|
|
70
|
-
self.inserts = [{}]*len(addrs) if inserts is None else inserts
|
|
71
|
-
self.smtp_cfg,self.test = smtp_cfg,test
|
|
72
|
-
|
|
73
|
-
def send_msgs(self, pause=0.5):
|
|
74
|
-
"Send all unsent messages to `addrs` with `pause` secs between each send"
|
|
75
|
-
with smtp_connection(**self.smtp_cfg) as conn:
|
|
76
|
-
while self.i < len(self.addrs):
|
|
77
|
-
addr,insert = self.addrs[self.i],self.inserts[self.i]
|
|
78
|
-
msg = self.msg.format(**insert)
|
|
79
|
-
eml = md2email(self.subj, self.from_addr, addr, md=msg)
|
|
80
|
-
if self.test: print(f"To: {addr}\n{'-'*40}\n{msg}\n{'='*40}\n")
|
|
81
|
-
else: conn.send_message(eml); sleep(pause)
|
|
82
|
-
self.i += 1
|
|
83
|
-
if self.i%100==0: print(self.i)
|
|
84
|
-
|
|
85
|
-
def reset(self): self.i=0
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=64.0"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name="markdown_merge"
|
|
7
|
-
requires-python=">=3.10"
|
|
8
|
-
dynamic = [ "keywords", "description", "version", "dependencies", "optional-dependencies", "readme", "license", "authors", "classifiers", "entry-points", "scripts", "urls"]
|
|
9
|
-
|
|
10
|
-
[tool.uv]
|
|
11
|
-
cache-keys = [{ file = "pyproject.toml" }, { file = "settings.ini" }, { file = "setup.py" }]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|