フラスコメガチュヌトリアル、パヌト7゚ラヌ凊理2018幎版

blog.miguelgrinberg.com


ミゲル・グリンバヌグ




<<<前 次>>>


この蚘事は、ミゲル・グリヌンバヌグによる教科曞の新版の第7郚の翻蚳であり、著者はこの出版物を2018幎5月に完成させる予定です。


私の偎では、翻蚳に぀いおいく぀もりです。




これは、Flask Mega-Tutorialシリヌズの第7章で、Flaskアプリケヌションで゚ラヌ凊理を凊理する方法を説明したす。


参考たでに、以䞋はこのシリヌズの蚘事のリストです。


目次

泚1このコヌスの叀いバヌゞョンをお探しの堎合は、こちらをご芧ください 。


泚2突然、このブログの私のミゲルの仕事を支持しお声を䞊げたい堎合、たたは単に蚘事を1週間埅぀忍耐がない堎合、私ミゲルグリヌンバヌグはこのガむドの完党版にパッケヌゞ化された電子曞籍たたはビデオを提䟛したす。 詳现に぀いおは、 learn.miguelgrinberg.comをご芧ください 。


この章では、新しい関数のコヌディングから埮生物孊的アプリケヌションに移り、代わりに、゜フトりェアプロゞェクトで垞に発生する゚ラヌに察凊するためのいく぀かの戊略に぀いお説明したす。 このトピックを説明するために、 第6章で远加したコヌドを意図的に誀りたした。 読み続ける前に、芋぀けられるかどうかを確認しおください


この章のGitHubリンク Browse 、 Zip 、 Diff 。


Flaskでの゚ラヌ凊理


Flaskアプリケヌションで゚ラヌが発生するずどうなりたすか 芋぀けるための最良の方法は、自分でそれを䜓隓するこずです。 アプリケヌションを起動し、少なくずも2人のナヌザヌが登録されおいるこずを確認しおください。 ナヌザヌの1人ずしおログむンし、プロファむルペヌゞを開いお[線集]リンクをクリックしたす。 プロファむル゚ディタヌで、ナヌザヌ名を既に登録されおいる別のナヌザヌの既存の名前に倉曎し、修正を適甚しおみおください。 これにより、「内郚サヌバヌ゚ラヌ」ずいう恐ろしいペヌゞが衚瀺されたす。



アプリケヌションが実行されおいるタヌミナルセッションで、゚ラヌスタックのトレヌスが衚瀺されたす。 スタックトレヌスは、゚ラヌの原因ずなった行に至るたで、このスタック䞊の䞀連の呌び出しを衚瀺するため、゚ラヌのデバッグ時に非垞に圹立ちたす。


(venv) $ flask run * Serving Flask app "microblog" * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) [2017-09-14 22:40:02,027] ERROR in app: Exception on /edit_profile [POST] Traceback (most recent call last): File "/home/miguel/microblog/venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1182, in _execute_context context) File "/home/miguel/microblog/venv/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 470, in do_execute cursor.execute(statement, parameters) sqlite3.IntegrityError: UNIQUE constraint failed: user.username 

スタックトレヌスは、゚ラヌの原因を瀺したす。 アプリケヌションは、ナヌザヌが新しいナヌザヌ名が既にシステムにある別のナヌザヌず䞀臎しないこずを確認せずにナヌザヌ名を倉曎するこずを蚱可したす。 この゚ラヌは、新しいナヌザヌ名をデヌタベヌスに曞き蟌もうずするSQLAlchemyから発生したすが、ナヌザヌ名列がunique = Trueで定矩されおいるため、デヌタベヌスはそれを拒吊したす。


ナヌザヌに衚瀺される゚ラヌペヌゞに゚ラヌに関する倚くの情報が含たれおいないこずが重芁であり、これは正しいです。 私は間違いなく、事故がデヌタベヌス゚ラヌたたは私が䜿甚しおいるデヌタベヌス、およびデヌタベヌス内のテヌブルずフィヌルドの名前が原因であるこずをナヌザヌに知らせたくありたせん。 この情報はすべお内郚のものでなければなりたせん。


理想ずはほど遠いものがいく぀かありたす。 pageい゚ラヌがあり、アプリケヌションのレむアりトず䞀臎しないペヌゞがありたす。 たた、タヌミナルにダンプされる重芁なアプリケヌションスタックトレヌスがあり、゚ラヌを芋逃さないように垞に確認する必芁がありたす。 そしおもちろん間違いがありたす。 これらすべおの問題を解決したすが、最初にFlaskデバッグモヌドに぀いお説明したしょう。


デバッグモヌド


䞊蚘の゚ラヌの凊理方法は、運甚サヌバヌで実行されるシステムに最適です。 ゚ラヌがある堎合、ナヌザヌには未定矩の゚ラヌのペヌゞが衚瀺されたすがこの゚ラヌペヌゞをもっず楜しくする぀もりです、重芁な゚ラヌデヌタはサヌバヌ出力たたはログファむルにありたす。


ただし、アプリケヌションを開発しおいるずきは、デバッグモヌドをオンにするこずができたす。デバッグモヌドは、Flaskがブラりザに盎接非垞に優れたデバッガを衚瀺するモヌドです。 デバッグモヌドをアクティブにするには、アプリケヌションを停止し、次の環境倉数を蚭定したす。


 (venv) $ export FLASK_DEBUG=1 

Microsoft Windowsを実行しおいる堎合は、゚クスポヌトの代わりにsetを䜿甚しおください。


FLASK_DEBUGを蚭定したら、サヌバヌを再起動したす。 端末の行は、芋慣れたものずは少し異なりたす。


 (venv) microblog2 $ flask run * Serving Flask app "microblog" * Forcing debug mode on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 177-562-960 

次に、アプリケヌションを再床クラッシュさせお、ブラりザで察話型デバッガヌを確認したす。



デバッガヌを䜿甚するず、スタックの各レベルを展開し、察応する゜ヌスコヌドを衚瀺できたす。 たた、任意のフレヌムでPythonを開き、有効なPython匏を実行しお、倉数の倀を確認するこずもできたす。


本番サヌバヌでは、Flaskアプリケヌションをデバッグモヌドで実行しないでください。 デバッガヌを䜿甚するず、サヌバヌ䞊でコヌドをリモヌトで実行できるため、アプリケヌションやサヌバヌに䟵入したい攻撃者にずっお予期しない莈り物になる可胜性がありたす。 远加のセキュリティ察策ずしお、ブラりザヌで実行䞭のデバッガヌが閉じられ、最初の䜿甚時に、Pinkコヌドが芁求されたす。これは、 flask runコマンドの出力で確認できたす。


デバッグモヌドに぀いお説明しおいるので、デバッグモヌドで有効になる2番目の重芁な機胜である再起動に぀いお蚀及する必芁がありたす。 これは、゜ヌスファむルが倉曎されたずきにアプリケヌションを自動的に再起動する非垞に䟿利な開発機胜です。 flask runをデバッグモヌドで実行するず、アプリケヌションで䜜業を続けるこずができ、ファむルを保存するたびにアプリケヌションが再起動しお新しいコヌドを取埗したす。


カスタム゚ラヌペヌゞ


Flaskは、カスタム゚ラヌペヌゞを䜜成するためのアプリケヌションメカニズムを提䟛するため、ナヌザヌは単玔で退屈なデフォルトを芋る必芁がありたせん。 䟋ずしお、最も䞀般的な2぀のHTTP゚ラヌ404および500のカスタム゚ラヌペヌゞを定矩したしょう。 他の゚ラヌのペヌゞの定矩も同じように機胜したす。


カスタム゚ラヌハンドラを宣蚀するには、 @errorhandlerデコレヌタを@errorhandlerたす。 新しいapp / errors.pyモゞュヌルに゚ラヌハンドラを配眮したす 。


 from flask import render_template from app import app, db @app.errorhandler(404) def not_found_error(error): return render_template('404.html'), 404 @app.errorhandler(500) def internal_error(error): db.session.rollback() return render_template('500.html'), 500 

゚ラヌ関数は衚瀺関数ず同様に機胜したす。 これらの2぀の゚ラヌに぀いおは、それぞれのテンプレヌトの内容を返したす。 䞡方の関数が、パタヌンの埌の2番目の倀、぀たり゚ラヌコヌド番号を返すこずに泚意しおください。 これたでに䜜成したすべおのプレれンテヌション関数に぀いお、デフォルトで200正垞終了のステヌタスコヌドが必芁なため、2番目の戻り倀を远加する必芁はありたせんでした。 これらは珟圚゚ラヌペヌゞであるため、応答ステヌタスコヌドにこれを反映させる必芁がありたす。


500番目の゚ラヌの゚ラヌハンドラは、デヌタベヌス障害が発生した埌に呌び出すこずができたす。これは、実際には重耇したナヌザヌ名の意図的なケヌスが原因です。 倱敗したデヌタベヌスセッションがテンプレヌトによるデヌタベヌスアクセスに干枉しないように、セッションロヌルバックを発行したす。 これにより、セッションがクリヌンな状態にリセットされたす。


゚ラヌ404のテンプレヌトは次のずおりです。


 {% extends "base.html" %} {% block content %} <h1>File Not Found</h1> <p><a href="{{ url_for('index') }}">Back</a></p> {% endblock %} 

そしお、これは500゚ラヌの1぀です。


 {% extends "base.html" %} {% block content %} <h1>An unexpected error has occurred</h1> <p>The administrator has been notified. Sorry for the inconvenience!</p> <p><a href="{{ url_for('index') }}">Back</a></p> {% endblock %} 

䞡方のテンプレヌトはbase.htmlテンプレヌトを継承するため、゚ラヌペヌゞは通垞のアプリケヌションペヌゞず同じ倖芳になりたす。


これらの゚ラヌハンドラヌをFlaskに登録するには、アプリケヌションむンスタンスの䜜成埌に新しいapp/errors.pyをむンポヌトする必芁がありたす。


 # ... from app import routes, models, errors 

タヌミナルセッションでFLASK_DEBUG = 0を蚭定した埌、再床ナヌザヌ名の重耇゚ラヌが発生した堎合は、より良い゚ラヌペヌゞが衚瀺されたす。



たたはそう 挔習ずしお自分で䜕かを考え出すこずをお勧めしたす。



メヌル゚ラヌ


Flaskが提䟛するデフォルトの゚ラヌ凊理のもう1぀の問題は、通知がないこずです。 ゚ラヌスタックトレヌスは端末に出力されたす。぀たり、サヌバヌプロセスの出力を゚ラヌ怜出のために監芖する必芁がありたす。 開発䞭にアプリケヌションを起動する堎合、これは正垞ですが、アプリケヌションが運甚サヌバヌにデプロむされるずすぐに誰も結果を確認しないため、より信頌性の高い゜リュヌションを䜜成する必芁がありたす。


゚ラヌに積極的に察応するこずは非垞に重芁だず思いたす。 アプリケヌションの補品版で゚ラヌが発生した堎合、すぐに知りたいです。 したがっお、最初の決定は、電子メヌルメッセヌゞの゚ラヌスタックトレヌスで゚ラヌが発生した盎埌に電子メヌルを送信するようにFlaskを構成するこずです。


最初のステップは、構成ファむルに電子メヌルサヌバヌデヌタを远加するこずです。


 class Config(object): # ... MAIL_SERVER = os.environ.get('MAIL_SERVER') MAIL_PORT = int(os.environ.get('MAIL_PORT') or 25) MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS') is not None MAIL_USERNAME = os.environ.get('MAIL_USERNAME') MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD') ADMINS = ['your-email@example.com'] 

電子メヌル構成倉数には、サヌバヌずポヌト、暗号化された接続を有効にするフラグ、オプションのナヌザヌ名ずパスワヌドが含たれたす。 環境倉数ぞのマッピングから5぀の構成倉数が掟生したす。 電子メヌルサヌバヌが環境にむンストヌルされおいない堎合、これを電子メヌル゚ラヌを無効にする必芁があるずいうサむンずしお䜿甚したす。 メヌルサヌバヌのポヌトも環境倉数で指定できたすが、蚭定されおいない堎合は、暙準ポヌト25が䜿甚されたす。メヌルサヌバヌの資栌情報はデフォルトでは䜿甚されたせんが、必芁に応じお提䟛できたす。 ADMINS構成ADMINSは、゚ラヌレポヌトを受信する電子メヌルアドレスのリストであるため、独自の電子メヌルアドレスがそのリストに含たれおいる必芁がありたす。


FlaskはPython loggingパッケヌゞを䜿甚しおログを保持したすが、このパッケヌゞにはすでにログをメヌルで送信する機胜がありたす。 ゚ラヌを含む電子メヌルを送信するために私がしなければならないこずは、 app.loggerであるFlaskログオブゞェクトにSMTPHandlerむンスタンスを远加するこずです。


 import logging from logging.handlers import SMTPHandler # ... if not app.debug: if app.config['MAIL_SERVER']: auth = None if app.config['MAIL_USERNAME'] or app.config['MAIL_PASSWORD']: auth = (app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']) secure = None if app.config['MAIL_USE_TLS']: secure = () mail_handler = SMTPHandler( mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']), fromaddr='no-reply@' + app.config['MAIL_SERVER'], toaddrs=app.config['ADMINS'], subject='Microblog Failure', credentials=auth, secure=secure) mail_handler.setLevel(logging.ERROR) app.logger.addHandler(mail_handler) 

ご芧のずおり、アプリケヌションがapp.debugでTrueずしお定矩されおいるデバッグモヌドなしでアプリケヌションが実行される堎合、および構成に電子メヌルサヌバヌが存圚する堎合にのみ、電子メヌルロガヌをオンにしたした。


メヌルレゞストラのセットアップは、倚くの電子メヌルサヌバヌに存圚する远加のセキュリティ蚭定を凊理する必芁があるため、倚少面倒です。 ただし、本質的に、䞊蚘のコヌドはSMTPHandlerむンスタンスを䜜成し、譊告、情報、デバッグメッセヌゞではなく゚ラヌメッセヌゞのみを送信するようにレベルを蚭定し、Flaskからapp.logger最終的に添付したす。


この関数の機胜を怜蚌するには、2぀のアプロヌチがありたす。 最も簡単な方法は、PythonのSMTPデバッグサヌバヌを䜿甚するこずです。 これは電子メヌルメッセヌゞを受け入れる停のメヌルサヌバヌですが、送信する代わりにコン゜ヌルに衚瀺したす。 このサヌバヌを起動するには、2番目のタヌミナルセッションを開き、次のコマンドを実行したす。


 (venv) $ python -m smtpd -n -c DebuggingServer localhost:8025 

デバッグSMTPサヌバヌを実行したたたにしお、最初のタヌミナルに戻り、 export MAIL_SERVER = localhostおよびMAIL_PORT = 8025をsetしたすMicrosoft Windowsを䜿甚しおいる堎合は、 export代わりにsetを䜿甚したす。 アプリケヌションはデバッグモヌドで電子メヌルを送信しないため、 FLASK_DEBUG倉数FLASK_DEBUG 0蚭定されおいるか、たったく蚭定されおいないこずを確認しおください。
アプリケヌションを起動し、SQLAlchemy゚ラヌを再床発生させお、停メヌルサヌバヌを実行しおいるタヌミナルセッションが゚ラヌスタックの党内容を含む電子メヌルをどのように衚瀺するかを確認したす。


この機胜の2番目のテスト方法は、実際のメヌルサヌバヌを構成するこずです。 以䞋は、 Gmailアカりントにメヌルサヌバヌを䜿甚するための構成です。


 export MAIL_SERVER=smtp.googlemail.com export MAIL_PORT=587 export MAIL_USE_TLS=1 export MAIL_USERNAME=<your-gmail-username> export MAIL_PASSWORD=<your-gmail-password> 

Microsoft Windowsを䜿甚しおいる堎合は、䞊蚘の各手順でexport代わりにsetを䜿甚しおください。


Gmailアカりントぞのアクセスを「安党性の䜎いアプリ」に明瀺的に蚱可しない限り、Gmailアカりントのセキュリティ機胜により、アプリケヌションからのメヌル送信が劚げられる堎合がありたす。 これに぀いおはここで読むこずができたす。アカりントのセキュリティが心配な堎合は、電子メヌルをチェックするためだけに構成するセカンダリアカりントを䜜成するか、このテストの間、安党性の䜎いアプリケヌションのアクセス蚱可を䞀時的に有効にしおからデフォルトに戻すこずができたす。


ファむルぞのロギング


電子メヌルの゚ラヌを取埗するこずは䟿利ですが、時には十分ではありたせん。 Pythonの䟋倖では説明されおおらず、重倧な問題ではない障害の堎合もありたすが、デバッグの目的で保存できるほど興味深い堎合がありたす。 このため、アプリケヌションのログファむルもサポヌトしたす。


今回はRotatingFileHandlerタむプの別のハンドラヌのロギングを有効にするには、電子メヌルハンドラヌず同様にアプリケヌションのロガヌを有効RotatingFileHandlerする必芁がありたす。


 # ... from logging.handlers import RotatingFileHandler import os # ... if not app.debug: # ... if not os.path.exists('logs'): os.mkdir('logs') file_handler = RotatingFileHandler('logs/microblog.log', maxBytes=10240, backupCount=10) file_handler.setFormatter(logging.Formatter( '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]')) file_handler.setLevel(logging.INFO) app.logger.addHandler(file_handler) app.logger.setLevel(logging.INFO) app.logger.info('Microblog startup') 

microblog.logずいうログファむルを、䜜成しおいないログディレクトリに䜜成しおいたす。


RotatingFileHandlerクラスは、ログを䞊曞きし、アプリケヌションが長時間実行されおいる堎合にログファむルが倧きくなりすぎないようにするので䟿利です。 この堎合、ログファむルのサむズを10 KBに制限し、最埌の10個のログファむルをバックアップずしお保存したす。


logging.Formatterクラスは、ログメッセヌゞ圢匏のカスタマむズを提䟛したす。 これらのメッセヌゞはファむルに送信されるため、できるだけ倚くの情報を含めるようにしたす。 そのため、タむムスタンプ、ログレベル、
メッセヌゞ、゜ヌスファむル、およびログ゚ントリの発信元の行番号。


ロギングをさらに䟿利にするために、アプリケヌションロガヌずファむルハンドラヌの䞡方で、ロギングレベルをINFOカテゎリに䞋げたす。 仕蚳カテゎリに粟通しおいない堎合、これらは重倧床の昇順でDEBUG 、 INFO 、 WARNING 、 ERRORおよびCRITICALです。


ログファむルの最初の䟿利な䜿甚法ずしお、サヌバヌは起動するたびにログに行を曞き蟌みたす。 実皌働サヌバヌでアプリケヌションを実行するず、これらのログ゚ントリはサヌバヌが再起動された時期を瀺したす。


ナヌザヌ名の修正


ナヌザヌ名の重耇゚ラヌが長すぎたす このような゚ラヌを凊理するアプリケヌションを準備する方法を瀺したので、ようやく修正するこずができたす。


芚えおいるなら、 RegistrationFormすでにナヌザヌ名をチェックしおいたすが、線集フォヌムの芁件は少し異なりたす。 登録時に、フォヌムに入力したナヌザヌ名がデヌタベヌスに存圚しないこずを確認する必芁がありたす。 プロファむルの線集フォヌムでは、同じチェックを実行する必芁がありたすが、䟋倖が1぀ありたす。 ナヌザヌが元のナヌザヌ名をそのたた残した堎合、このナヌザヌ名は既にこのナヌザヌに割り圓おられおいるため、チェックで解決する必芁がありたす。 以䞋に、このフォヌムのナヌザヌ名チェックの実行方法を瀺したす。


 class EditProfileForm(FlaskForm): username = StringField('Username', validators=[DataRequired()]) about_me = TextAreaField('About me', validators=[Length(min=0, max=140)]) submit = SubmitField('Submit') def __init__(self, original_username, *args, **kwargs): super(EditProfileForm, self).__init__(*args, **kwargs) self.original_username = original_username def validate_username(self, username): if username.data != self.original_username: user = User.query.filter_by(username=self.username.data).first() if user is not None: raise ValidationError('Please use a different username.') 

実装は、元のナヌザヌ名を匕数ずしお取る、クラスのコンストラクタヌのスヌパヌ関数である特別な怜蚌メ゜ッドで実行されたす。 このナヌザヌ名はむンスタンス倉数ずしお保存され、 validate_username()メ゜ッドでvalidate_username()されたす。 フォヌムに入力されたナヌザヌ名が元のナヌザヌ名ず䞀臎する堎合、デヌタベヌスの重耇をチェックする理由はありたせん。


この新しい怜蚌メ゜ッドを䜿甚するには、フォヌムオブゞェクトが䜜成されるビュヌ関数に元のナヌザヌ名匕数を远加する必芁がありたす。


 @app.route('/edit_profile', methods=['GET', 'POST']) @login_required def edit_profile(): form = EditProfileForm(current_user.username) # ... 

これで゚ラヌが修正され、ほずんどの堎合、線集プロファむルの圢での重耇が防止されたす。 これは、2぀以䞊のプロセスがデヌタベヌスに同時にアクセスするず動䜜しない堎合があるため、理想的な゜リュヌションではありたせん。 この状況では、競合状態が怜蚌に぀ながる可胜性がありたすが、しばらくするず、デヌタベヌスの名前を倉曎しようずするず別のプロセスによっお既に倉曎されおおり、ナヌザヌの名前を倉曎できたせん。 倚くのサヌバヌプロセスを持぀非垞にビゞヌなアプリケヌションを陀いお、これはややありそうにないので、今のずころこれに぀いおは心配したせん。


この時点で、゚ラヌを再床再珟しお、フォヌム怜蚌メ゜ッドがどのように゚ラヌを防ぐかを確認できたす。


<<<前 次>>>


PS


゚ラヌ凊理


翻蚳者から


管理者ぞの゚ラヌメッセヌゞの受信をメヌルで確認するこずにしたした。 このため、 routes.pyモゞュヌルを台無しにしたした。 この非垞に「砎損」のため、 def edit_profile()前に@app.route('/edit_profile', methods=['GET', 'POST'])デコレヌタヌ@app.route('/edit_profile', methods=['GET', 'POST'])をコメントアりトしたした。 その結果、゚ラヌが発生し、すべおログファむルに蚘録されたしたが、手玙は届きたせんでした。 Python 3.3を䜿甚しおいたす。 おそらく、これは新しいバヌゞョンでは発生したせん。 しかし、ロシア語レむアりトのWindows 7では、これが起こりたした。


管理者にメッセヌゞを送信しようずしたずきに、アプリケヌションがメッセヌゞの䜜成䞭に゚ンコヌド゚ラヌを受け取りたした。 コン゜ヌルりィンドりには次の行が含たれおいたした。



ご芧のずおり、リンクは仮想環境ではなく、暙準のPythonのディレクトリを指したす。


バヌゞョン3でのloggingは暙準のPythonラむブラリであるため、 pipを䜿甚しおむンストヌルする必芁はありたせん。


暙準モゞュヌルに぀いお

たた、PyPIにあるロギングモゞュヌルは廃止されおおり、Python3互換ではありたせん。


READMEファむルによるず、その最新バヌゞョンは2005幎3月2日にリリヌスされたした。


したがっお、ロギングをむンストヌルしようずしないでください。
暙準ラむブラリの新しいモゞュヌルを圓然のこずず考えおください。 仮想ラむブラリで䜿甚するこずが重芁な堎合。


venvにコピヌした埌、 lib logging仮想環境からむンポヌトされたす



再び゚ラヌが発生したす



logging仮想になりたした。 ただし、 smtplib暙準です。


すべおのラむブラリを暙準環境から仮想環境にドラッグする必芁はないず思いたす。
これによる゚ラヌは消えたせん。


暙準のメヌルモゞュヌルに぀いお

メッセヌゞの゚ンコヌドの問題は、暙準のemailパッケヌゞを䜿甚しお、優先゚ンコヌドを瀺すメッセヌゞを䜜成するこずで解決されたす。


このパッケヌゞのむンタヌネットからの䟋を次に瀺したす。


 # -*- coding: utf-8 -*- from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import quopri def QuoHead(String): s = quopri.encodestring(String.encode('UTF-8'), 1, 0) return "=?utf-8?Q?" + s.decode('UTF-8') + "?=" FIOin = " " emailout = "some@test.ru" emailin = "some2@test.ru" msg = MIMEMultipart() msg["Subject"] = QuoHead("  " + FIOin).replace('=\n', '') msg["From"] = (QuoHead(" ") + " <" + emailout + ">").replace('=\n', '') msg["To"] = (QuoHead(FIOin) + " <" + emailin + ">").replace('=\n', '') m = """ .   . ,    .""" text = MIMEText(m.encode('utf-8'), 'plain', 'UTF-8') msg.attach(text) print(msg.as_string()) 

しかし、゚ラヌメッセヌゞを送信するためにそれをどのように適甚するのですか
誰かが蚘事のコメントで提案するかもしれたせん。


flask-mail . logging smtplib


. smtplib.py .


encode('utf-8')



, -, .




Source: https://habr.com/ru/post/J346880/


All Articles