Parcourir la source

upd: now code should work without Pandas

Mikael Koli il y a 4 ans
Parent
commit
732adae3e0
3 fichiers modifiés avec 21 ajouts et 10 suppressions
  1. 9 5
      redmail/email/attachment.py
  2. 10 4
      redmail/email/body.py
  3. 2 1
      redmail/email/utils.py

+ 9 - 5
redmail/email/attachment.py

@@ -8,8 +8,7 @@ import io
 from pathlib import Path, PurePath
 from pathlib import Path, PurePath
 from typing import Union
 from typing import Union
 
 
-from .utils import PIL, plt
-import pandas as pd
+from .utils import PIL, plt, pd
 
 
 
 
 class Attachments:
 class Attachments:
@@ -67,12 +66,17 @@ class Attachments:
             raise TypeError(f"Unknown attachment {type(item)}")
             raise TypeError(f"Unknown attachment {type(item)}")
 
 
     def _get_bytes_named(self, item, name:str) -> bytes:
     def _get_bytes_named(self, item, name:str) -> bytes:
+
+        has_pandas = pd is not None
+        has_pillow = PIL is not None
+        has_matplotlib = plt is not None
+
         if isinstance(item, str):
         if isinstance(item, str):
             # Considered as raw document
             # Considered as raw document
             return item
             return item
         elif isinstance(item, PurePath):
         elif isinstance(item, PurePath):
             return item.read_bytes()
             return item.read_bytes()
-        elif isinstance(item, (pd.DataFrame, pd.Series)):
+        elif has_pandas and isinstance(item, (pd.DataFrame, pd.Series)):
             buff = io.BytesIO()
             buff = io.BytesIO()
             if name.endswith(".xlsx"):
             if name.endswith(".xlsx"):
                 item.to_excel(buff)
                 item.to_excel(buff)
@@ -87,12 +91,12 @@ class Attachments:
                 raise ValueError(f"Unknown dataframe conversion for '{name}'")
                 raise ValueError(f"Unknown dataframe conversion for '{name}'")
         elif isinstance(item, (bytes, bytearray)):
         elif isinstance(item, (bytes, bytearray)):
             return item
             return item
-        elif PIL is not None and isinstance(item, PIL.Image.Image):
+        elif has_pillow and isinstance(item, PIL.Image.Image):
             buf = io.BytesIO()
             buf = io.BytesIO()
             item.save(buf, format='PNG')
             item.save(buf, format='PNG')
             buf.seek(0)
             buf.seek(0)
             return buf.read()
             return buf.read()
-        elif plt is not None and isinstance(item, plt.Figure):
+        elif has_matplotlib and isinstance(item, plt.Figure):
             buf = io.BytesIO()
             buf = io.BytesIO()
             item.savefig(buf, format=Path(name).suffix[1:])
             item.savefig(buf, format=Path(name).suffix[1:])
             buf.seek(0)
             buf.seek(0)

+ 10 - 4
redmail/email/body.py

@@ -2,7 +2,7 @@ from email.message import EmailMessage
 import mimetypes
 import mimetypes
 from io import BytesIO
 from io import BytesIO
 from pathlib import Path
 from pathlib import Path
-from typing import Dict, Union, ByteString
+from typing import TYPE_CHECKING, Dict, Union, ByteString
 from pathlib import Path
 from pathlib import Path
 
 
 
 
@@ -12,12 +12,15 @@ from redmail.utils import import_from_string
 from email.utils import make_msgid
 from email.utils import make_msgid
 
 
 from jinja2.environment import Template, Environment
 from jinja2.environment import Template, Environment
-import pandas as pd
 
 
 from markupsafe import Markup
 from markupsafe import Markup
 
 
 # We try to import matplotlib and PIL but if fails, they will be None
 # We try to import matplotlib and PIL but if fails, they will be None
-from .utils import PIL, plt
+from .utils import PIL, plt, pd
+
+if TYPE_CHECKING:
+    # For type hinting
+    from pandas import DataFrame
 
 
 class BodyImage:
 class BodyImage:
     "Utility class to represent image on HTML"
     "Utility class to represent image on HTML"
@@ -54,6 +57,9 @@ class Body:
         # TODO: Nicer tables. 
         # TODO: Nicer tables. 
         #   https://stackoverflow.com/a/55356741/13696660
         #   https://stackoverflow.com/a/55356741/13696660
         #   Email HTML (generally) does not support CSS
         #   Email HTML (generally) does not support CSS
+        if pd is None:
+            raise ImportError("Missing package 'pandas'. Prettifying tables requires Pandas.")
+        
         extra = {} if extra is None else extra
         extra = {} if extra is None else extra
         df = pd.DataFrame(tbl)
         df = pd.DataFrame(tbl)
 
 
@@ -119,7 +125,7 @@ class HTMLBody(Body):
             
             
             self.attach_imgs(html_msg, cid_path_mapping)
             self.attach_imgs(html_msg, cid_path_mapping)
 
 
-    def render(self, html:str, images:Dict[str, Union[dict, bytes, Path]]=None, tables:Dict[str, pd.DataFrame]=None, jinja_params:dict=None, domain=None):
+    def render(self, html:str, images:Dict[str, Union[dict, bytes, Path]]=None, tables:Dict[str, 'DataFrame']=None, jinja_params:dict=None, domain=None):
         """Render Email HTML body (sets cid for image sources and adds data as other parameters)
         """Render Email HTML body (sets cid for image sources and adds data as other parameters)
 
 
         Parameters
         Parameters

+ 2 - 1
redmail/email/utils.py

@@ -2,4 +2,5 @@
 from redmail.utils import import_from_string
 from redmail.utils import import_from_string
 
 
 plt = import_from_string("matplotlib.pyplot", if_missing="ignore")
 plt = import_from_string("matplotlib.pyplot", if_missing="ignore")
-PIL = import_from_string("PIL", if_missing="ignore")
+PIL = import_from_string("PIL", if_missing="ignore")
+pd = import_from_string("pandas", if_missing="ignore")