33
4455"""
6- from __future__ import print_function
76import sys
87import time
9- import codecs
108import textwrap
9+ from pathlib import Path
10+ import csv
1111import click
1212from .template_message import TemplateMessage
1313from .sendmail_client import SendmailClient
1414from . import exceptions
15- from . import utils
16-
17- # Python 2 pathlib support requires backport
18- try :
19- from pathlib2 import Path
20- except ImportError :
21- from pathlib import Path
22-
23- # Python 2 UTF8 support requires csv backport
24- try :
25- from backports import csv
26- except ImportError :
27- import csv
28-
29- # Python 2 UTF8 file redirection
30- # http://www.macfreek.nl/memory/Encoding_of_Python_stdout
31- if sys .stdout .encoding != 'UTF-8' and not hasattr (sys .stdout , "buffer" ):
32- sys .stdout = codecs .getwriter ('utf-8' )(sys .stdout , 'strict' )
3315
3416
3517@click .command (context_settings = {"help_option_names" : ['-h' , '--help' ]})
@@ -128,37 +110,28 @@ def main(sample, dry_run, limit, no_limit, resume,
128110 break
129111 time .sleep (1 )
130112 print_bright_white_on_cyan (
131- ">>> message {message_num}"
132- .format (message_num = message_num ),
113+ f">>> message { message_num } " ,
133114 output_format ,
134115 )
135116 print_message (message , output_format )
136117 print_bright_white_on_cyan (
137- ">>> message {message_num} sent"
138- .format (message_num = message_num ),
118+ f">>> message { message_num } sent" ,
139119 output_format ,
140120 )
141121 message_num += 1
142122
143123 except exceptions .MailmergeError as error :
144- hint_text = '\n Hint: "--resume {}"' .format (message_num )
145- sys .exit (
146- "Error on message {message_num}\n "
147- "{error}"
148- "{hint}"
149- .format (
150- message_num = message_num ,
151- error = error ,
152- hint = (hint_text if message_num > 1 else "" ),
153- )
154- )
124+ hint_text = ""
125+ if message_num > 1 :
126+ hint_text = f'\n Hint: "--resume { message_num } "'
127+ sys .exit (f"Error on message { message_num } \n { error } { hint_text } " )
155128
156129 # Hints for user
157130 if not no_limit :
131+ pluralizer = "" if limit == 1 else "s"
158132 print (
159- ">>> Limit was {limit} message{pluralizer}. "
133+ f ">>> Limit was { limit } message{ pluralizer } . "
160134 "To remove the limit, use the --no-limit option."
161- .format (limit = limit , pluralizer = ("" if limit == 1 else "s" ))
162135 )
163136 if dry_run :
164137 print (
@@ -180,40 +153,40 @@ def check_input_files(template_path, database_path, config_path, sample):
180153 sys .exit (0 )
181154
182155 if not template_path .exists ():
183- sys .exit (textwrap .dedent (u """\
156+ sys .exit (textwrap .dedent (f """\
184157 Error: can't find template "{ template_path } ".
185158
186159 Create a sample (--sample) or specify a file (--template).
187160
188161 See https://github.com/awdeorio/mailmerge for examples.\
189- """. format ( template_path = template_path ) ))
162+ """ ))
190163
191164 if not database_path .exists ():
192- sys .exit (textwrap .dedent (u """\
165+ sys .exit (textwrap .dedent (f """\
193166 Error: can't find database "{ database_path } ".
194167
195168 Create a sample (--sample) or specify a file (--database).
196169
197170 See https://github.com/awdeorio/mailmerge for examples.\
198- """. format ( database_path = database_path ) ))
171+ """ ))
199172
200173 if not config_path .exists ():
201- sys .exit (textwrap .dedent (u """\
174+ sys .exit (textwrap .dedent (f """\
202175 Error: can't find config "{ config_path } ".
203176
204177 Create a sample (--sample) or specify a file (--config).
205178
206179 See https://github.com/awdeorio/mailmerge for examples.\
207- """. format ( config_path = config_path ) ))
180+ """ ))
208181
209182
210183def create_sample_input_files (template_path , database_path , config_path ):
211184 """Create sample template, database and server config."""
212185 for path in [template_path , database_path , config_path ]:
213186 if path .exists ():
214- sys .exit ("Error: file exists: {}" . format ( path ) )
187+ sys .exit (f "Error: file exists: { path } " )
215188 with template_path .open ("w" ) as template_file :
216- template_file .write (textwrap .dedent (u """\
189+ template_file .write (textwrap .dedent ("""\
217190 TO: {{email}}
218191 SUBJECT: Testing mailmerge
219192@@ -223,13 +196,13 @@ def create_sample_input_files(template_path, database_path, config_path):
223196 Your number is {{number}}.
224197 """ ))
225198 with database_path .open ("w" ) as database_file :
226- database_file .write (textwrap .dedent (u """\
199+ database_file .write (textwrap .dedent ("""\
227200 email,name,number
228201229202230203 """ ))
231204 with config_path .open ("w" ) as config_file :
232- config_file .write (textwrap .dedent (u """\
205+ config_file .write (textwrap .dedent ("""\
233206 # Mailmerge SMTP Server Config
234207 # https://github.com/awdeorio/mailmerge
235208 #
@@ -273,17 +246,13 @@ def create_sample_input_files(template_path, database_path, config_path):
273246 # port = 25
274247 # ratelimit = 0
275248 """ ))
276- print (textwrap .dedent (u """\
249+ print (textwrap .dedent (f """\
277250 Created sample template email "{ template_path } "
278251 Created sample database "{ database_path } "
279252 Created sample config file "{ config_path } "
280253
281254 Edit these files, then run mailmerge again.\
282- """ .format (
283- template_path = template_path ,
284- database_path = database_path ,
285- config_path = config_path ,
286- )))
255+ """ ))
287256
288257
289258def read_csv_database (database_path ):
@@ -292,20 +261,25 @@ def read_csv_database(database_path):
292261 We'll use a class to modify the csv library's default dialect ('excel') to
293262 enable strict syntax checking. This will trigger errors for things like
294263 unclosed quotes.
264+
265+ We open the file with the utf-8-sig encoding, which skips a byte order mark
266+ (BOM), if any. Sometimes Excel will save CSV files with a BOM. See Issue
267+ #93 https://github.com/awdeorio/mailmerge/issues/93
268+
295269 """
296270 class StrictExcel (csv .excel ):
297271 # Our helper class is really simple
298272 # pylint: disable=too-few-public-methods, missing-class-docstring
299273 strict = True
300274
301- with database_path .open (mode = "r" , encoding = "utf-8" ) as database_file :
275+ with database_path .open (encoding = "utf-8-sig " ) as database_file :
302276 reader = csv .DictReader (database_file , dialect = StrictExcel )
303277 try :
304278 for row in reader :
305279 yield row
306280 except csv .Error as err :
307281 raise exceptions .MailmergeError (
308- "{ }:{}: {}" . format ( database_path , reader . line_num , err )
282+ f" { database_path } :{ reader . line_num } : { err } "
309283 )
310284
311285
@@ -343,11 +317,11 @@ def print_message(message, output_format):
343317 assert output_format in ["colorized" , "text" , "raw" ]
344318
345319 if output_format == "raw" :
346- print (utils . flatten_message ( message ) )
320+ print (message )
347321 return
348322
349323 for header , value in message .items ():
350- print (u "{header}: {value}". format ( header = header , value = value ) )
324+ print (f "{ header } : { value } " )
351325 print ()
352326 for part in message .walk ():
353327 if part .get_content_maintype () == "multipart" :
@@ -356,23 +330,20 @@ def print_message(message, output_format):
356330 if message .is_multipart ():
357331 # Only print message part dividers for multipart messages
358332 print_cyan (
359- ">>> message part: {content_type}"
360- .format (content_type = part .get_content_type ()),
333+ f">>> message part: { part .get_content_type ()} " ,
361334 output_format ,
362335 )
363336 charset = str (part .get_charset ())
364337 print (part .get_payload (decode = True ).decode (charset ))
365338 print ()
366339 elif is_attachment (part ):
367340 print_cyan (
368- ">>> message part: attachment {filename}"
369- .format (filename = part .get_filename ()),
341+ f">>> message part: attachment { part .get_filename ()} " ,
370342 output_format ,
371343 )
372344 else :
373345 print_cyan (
374- ">>> message part: {content_type}"
375- .format (content_type = part .get_content_type ()),
346+ f">>> message part: { part .get_content_type ()} " ,
376347 output_format ,
377348 )
378349
0 commit comments