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.

Files changed (23) hide show
  1. markdown_merge-0.1.5/MANIFEST.in +5 -0
  2. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/PKG-INFO +7 -13
  3. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/README.md +0 -5
  4. markdown_merge-0.1.5/markdown_merge/__init__.py +2 -0
  5. markdown_merge-0.1.5/markdown_merge/_modidx.py +19 -0
  6. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge/core.py +29 -23
  7. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/PKG-INFO +7 -13
  8. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/SOURCES.txt +2 -2
  9. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/requires.txt +1 -1
  10. markdown_merge-0.1.5/pyproject.toml +3 -0
  11. markdown_merge-0.1.5/settings.ini +39 -0
  12. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/setup.py +22 -21
  13. markdown_merge-0.1.0/markdown_merge/__init__.py +0 -2
  14. markdown_merge-0.1.0/markdown_merge/_modidx.py +0 -37
  15. markdown_merge-0.1.0/markdown_merge/_nbdev.py +0 -9
  16. markdown_merge-0.1.0/markdown_merge/markdown_merge.py +0 -85
  17. markdown_merge-0.1.0/pyproject.toml +0 -11
  18. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/LICENSE +0 -0
  19. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/dependency_links.txt +0 -0
  20. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/entry_points.txt +0 -0
  21. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/not-zip-safe +0 -0
  22. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/markdown_merge.egg-info/top_level.txt +0 -0
  23. {markdown_merge-0.1.0 → markdown_merge-0.1.5}/setup.cfg +0 -0
@@ -0,0 +1,5 @@
1
+ include settings.ini
2
+ include LICENSE
3
+ include CONTRIBUTING.md
4
+ include README.md
5
+ recursive-exclude * __pycache__
@@ -1,24 +1,23 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: markdown_merge
3
- Version: 0.1.0
4
- Summary: Send templated emails in markdown
5
- Home-page: https://github.com/answerdotai/markdown_merge/tree/master/
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: info@fast.ai
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 :: 3 - Alpha
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: Programming Language :: Python :: 3.13
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: packaging
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`
@@ -3,11 +3,6 @@
3
3
 
4
4
  <!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
5
5
 
6
- ``` python
7
- #hide
8
- from markdown_merge.markdown_merge import *
9
- ```
10
-
11
6
  ## Install
12
7
 
13
8
  `pip install markdown_merge`
@@ -0,0 +1,2 @@
1
+ __version__ = "0.1.5"
2
+ from .core import *
@@ -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 2
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 6
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 9
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 11
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 13
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 23
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
- "Context manager for SMTP connection"
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
- try: yield conn
63
- finally: conn.quit()
63
+ return conn
64
64
 
65
- # %% ../00_core.ipynb 27
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.5):
73
+ def send_msgs(self, pause=0.2):
74
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)
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.0
4
- Summary: Send templated emails in markdown
5
- Home-page: https://github.com/answerdotai/markdown_merge/tree/master/
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: info@fast.ai
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 :: 3 - Alpha
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: Programming Language :: Python :: 3.13
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: packaging
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
@@ -1,4 +1,4 @@
1
- packaging
1
+ fastcore
2
2
  markdown
3
3
 
4
4
  [dev]
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64.0"]
3
+ build-backend = "setuptools.build_meta"
@@ -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,re,sys
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.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13'.split()
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 = ['packaging']
30
- if cfg.get('requirements'): requirements += cfg.get('requirements','').split()
31
- if cfg.get('pip_requirements'): requirements += cfg.get('pip_requirements','').split()
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
- long_description = open('README.md', encoding="utf8").read()
35
- # ![png](docs/images/output_13_0.png)
36
- for ext in ['png', 'svg']:
37
- long_description = re.sub(r'!\['+ext+'\]\((.*)\)', '!['+ext+']('+'https://raw.githubusercontent.com/{}/{}'.format(cfg['user'],cfg['lib_name'])+'/'+cfg['branch']+'/\\1)', long_description)
38
- long_description = re.sub(r'src=\"(.*)\.'+ext+'\"', 'src=\"https://raw.githubusercontent.com/{}/{}'.format(cfg['user'],cfg['lib_name'])+'/'+cfg['branch']+'/\\1.'+ext+'\"', long_description)
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 = 'markdown-merge',
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 = 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,2 +0,0 @@
1
- __version__ = "0.1.0"
2
- from .core import *
@@ -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,9 +0,0 @@
1
- #AUTOGENERATED BY NBDEV! DO NOT EDIT!
2
-
3
- __all__ = ["index", "modules"]
4
-
5
- index = {"get_addr": "00_markdown_merge.ipynb",
6
- "md2email": "00_markdown_merge.ipynb",
7
- "MarkdownMerge": "00_markdown_merge.ipynb"}
8
-
9
- modules = ["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