ç§ã¯èªåã®ããŒãžã§ã³ã®gunicornãåºå®ããã®ãæ ã£ãŠããŸããããä»æãã¢ããªãåãããã€ãããšå®è¡ã³ãã³ããå£ãå§ããèªåçã«20.0ã«ã¢ããã°ã¬ãŒããããŸããã
ç§ã®ããŒãžã§ã³ã®gunicornã19.9ã«ããŠã³ã°ã¬ãŒããããšãåé¡ãä¿®æ£ãããŸããã
ããã¯ãã¢ããªãå®è¡ããããã«äœ¿çšããŠããã³ãã³ãã§ãã
gunicorn 'app:create_app()' --workers 4 --threads 4 --bind 0.0.0.0:$PORT
ãšã©ãŒã¯æ¬¡ã®ãšããã§ãã
Failed to find application object 'create_app()' in 'app'
ç§ããã®åé¡ãçµéšããŸãããããªãã¡Failed to find application object 'create_app()' in 'app'
ããŒãžã§ã³19.9.0ã«åºå®ãããšãåé¡ã解決ããŸãã
ç§ã¯åœåãä¿®æ£ã¯gunicornã³ãã³ãã次ã®ããã«å€æŽããããšã§ãããïŒ
gunicorn --bind 0.0.0.0:$PORT app:create_app()
ã«ïŒ
gunicorn --bind 0.0.0.0:$PORT app:create_app
ïŒcreate_appã®åŸã®è§ãã£ãããªããªã£ãããšã«æ³šæããŠãã ããïŒã æåã¯ããã¹ãŠãé 調ã«èŠããŸãã
website_1 | [2019-11-10 19:18:54 +0000] [1] [INFO] gunicorn20.0.0ãéå§ããŠããŸã
website_1 | [2019-11-10 19:18:54 +0000] [1] [æ å ±]ãªã¹ãã³ã°ïŒ http ïŒ
website_1 | [2019-11-10 19:18:54 +0000] [1] [æ å ±]ã¯ãŒã«ãŒã®äœ¿çšïŒåæ
website_1 | [2019-11-10 19:18:54 +0000] [11] [INFO] pidã§ã¯ãŒã«ãŒãèµ·åããŠããŸãïŒ11
ããããæ®å¿µãªããããã©ã¹ã³ã®Webãµã€ã/ãšã³ããã€ã³ããããŒãããããšãããšã次ã®ããã«è¡šç€ºããããããããã¯èæ°æ¥Œã«ãããŸããã
[2019-11-10 19:20:28 +0000] [11] [ãšã©ãŒ]ãªã¯ãšã¹ãã®åŠçäžã«ãšã©ãŒãçºçããŸãã/
website_1 | ãã¬ãŒã¹ããã¯ïŒæåŸã®æåŸã®åŒã³åºãïŒïŒ
website_1 | ãã¡ã€ã«ã/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.pyãã134è¡ç®ããã³ãã«
website_1 | self.handle_requestïŒlistenerãreqãclientãaddrïŒ
website_1 | ãã¡ã€ã«ã/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.pyãã175è¡ç®ãhandle_request
website_1 | respiter = self.wsgiïŒenvironãresp.start_responseïŒ
website_1 | TypeErrorïŒcreate_appïŒïŒã¯0åã®äœçœ®åŒæ°ãåããŸããã2åãæå®ãããŸãã
ããã¯æããã«gunicornããŒãžã§ã³20.0.0ã®åé¡ã§ãã
ãã®å€æŽã«é¢é£ããŠããå¿ èŠããããŸãïŒ https ïŒ
ããªãã®åŽã§ãããä¿®æ£ãã1ã€ã®æ¹æ³ã¯ãã¡ã€ã³ã¢ãžã¥ãŒã«ã§myapp = create_app()
ããšã¯ã¹ããŒããã10åãapp:myapp
éå§ããããšã§ãã ããã¯æ©èœããã¯ãã§ããæ©èœããªãå Žåã¯ãç¥ãããã ããã
ããã§äœãããå¿
èŠããããã©ãã調ã¹ãŸãã @berkerpeksagãªãeval
ã®åé€ãå¿
èŠã ã£ãã®ã§ããïŒ
ããã¯ããã®å€æŽã«é¢é£ããŠããå¿ èŠããããŸãïŒ 3701ad9ïŒdiff-0b90f794c3e9742c45bf484505e3db8dR377 viaïŒ2043ã
ããªãã®åŽã§ãããä¿®æ£ãã1ã€ã®æ¹æ³ã¯ãã¡ã€ã³ã¢ãžã¥ãŒã«ã§
myapp = create_app()
ããšã¯ã¹ããŒããã10åãapp:myapp
éå§ããããšã§ãã ããã¯æ©èœããã¯ãã§ããæ©èœããªãå Žåã¯ãç¥ãããã ãããããã§äœãããå¿ èŠããããã©ãã調ã¹ãŸãã @berkerpeksagãªã
eval
ã®åé€ãå¿ èŠã ã£ãã®ã§ããïŒ
ã¢ããªã±ãŒã·ã§ã³ã§ãã®å€æŽãè¡ããã¯ã©ãã·ã¥ãä¿®æ£ããŸãããGunicornã¯ã create_app()
ã®çµæãå€æ°ã«ä¿åããGunicornã®å®è¡ã³ãã³ãã§äœ¿çšã§ããããã«ãšã¯ã¹ããŒãããããšã§ãã¢ããªã±ãŒã·ã§ã³ãå®è¡ã§ããããã«ãªããŸããã
# app.py
def create_app():
...
my_app = create_app()
gunicorn "app:my_app" --workers 8
@benoitcãš@ jrusso1020ãäžèšã§ææ¡ããããšãå®è¡ãããšãåé¡ãä¿®æ£ãããããšã確èªã§ããŸãã çããããããšãïŒ
次ã®ããã«èµ·åæã«ãã©ã¡ãŒã¿ãŒã®åãæž¡ããå¿ èŠãªå Žåãä¿®æ£ãæ©èœããŠããªãããã§ãã
gunicorn --chdir hcli_core path
"hcli_ coreïŒHCLI ïŒ" hcli_core sample hfm
"ïŒ.connector"ã
ãã©ã¡ãŒã¿ã®åãæž¡ãã¯19.9.0ã§æ©èœããŸããã20.0.0ã§ã¯å€±æããŸãã
@benoitcç¥ã£ãŠãããšäŸ¿å©ãªå Žåã«åããŠããã©ã¹ã³ã®ããã¥ã¡ã³ãã§ã¯ãgunicornã䜿çšãããšãã«app:create_app()
ãã¿ãŒã³ãæšå¥šããŠããŸãã æ°ãããŠãŒã¶ãŒã®äžã«ã¯ããã©ã¹ã³ã¢ããªãäœæããçµæãæåã«gunicornãè©ŠããŠã¿ã人ããããšæããŸãã圌ãã¯ããããã®ããã¥ã¡ã³ãããã®çŸåšå£ããŠããæšå¥šäºé
ã䜿çšããããšããŸãïŒå°ãªããšãç§ã®çµéšã§ã¯ãããŸãïŒã
ãã®ããŒã ã«é£çµ¡ããŠæŽæ°ãäŸé Œããããšã¯ã§ããŸããã @ berkerpeksagãexec
ã®ãããããæ€èšããã®ãåŸ
ã£ãŠããããå
ã«æ»ãããšãçã«ããªã£ãŠããå Žåã«åããŸãã
@ tjwaterman99ãããšããã®æ¹æ³ã§ã¢ããªã«åŒæ°ãæž¡ãã®ã奜ããã©ããã¯
Flaskã®äœ¿çšæ³ã®ç§ãã¡èªèº«ã®
cc @tilgovi @berkerpeksag ^^
FWIWç§ãã¡ãããã«ééããŠããŸãã
ãã¢ããªã±ãŒã·ã§ã³ãã¡ã¯ããªãã®Flaskãã¿ãŒã³ã«åŸã人ã¯ããªãå€ããšæããŸãã
確ãã«åé¿çã¯ãããŸãããå°ãªããšãå€æŽãã°ã«ã¯ããããé倧ãªå€æŽãšããŠèšèŒããå¿
èŠããããŸãã
app:callable()
ãapp:callable(some, args)
ãªã©ã®äœ¿çšæ³ãæå³çã«ãµããŒãããããšã¯ãªããšæããŸãã 以åã®å®è£
ã§eval()
ã䜿çšããããšã«ããäžå¹žãªå¯äœçšã ã£ããšæããŸãã
çŸåšã®å®è£
ã¯ãDjangoã®import_string()
æ©èœã«ããªãè¿ããã®ã§ãã
https://github.com/django/django/blob/master/django/utils/module_loading.py#L7 -L24
ããã¥ã¡ã³ããæ¹åãããªãªãŒã¹ããŒããè¿œå ãããã説æçãªãšã©ãŒã¡ãã»ãŒãžã衚瀺ãããŠããã ããŸãã
appïŒcallable ïŒïŒãappïŒcallable ïŒsomeãargsïŒãªã©ã®äœ¿çšæ³ãæå³çã«ãµããŒãããããšã¯ãªããšæããŸãã 以åã®å®è£ ã§evalïŒïŒã䜿çšããããšã«ããäžå¹žãªå¯äœçšã ã£ããšæããŸãã
ã¯ãç§ã¯åæããã ç§ãèŠãŠããéããã¢ããªã±ãŒã·ã§ã³ãèµ·åãããã®ãããªæ¹æ³ããµããŒãããããšã¯ãããŸããã
ããæ確ãªãšã©ãŒã®ããã«ç§ã¯+1ã§ãã ã¢ããªã±ãŒã·ã§ã³åãåçŽãªååã§ãªãå Žåã¯ããšã©ãŒãçºçããå¯èœæ§ããããŸããïŒ
ããã¯ãäž»èŠãªwsgiãã¬ãŒã ã¯ãŒã¯ã®1ã€ïŒãã©ã¹ã³ïŒã®å ¬éããã¥ã¡ã³ãã«èšèŒãããŠããæ瀺çãªåäœã§ããã以åã¯ãããžã§ã¯ãã§ãµããŒããããŠããããšãèŠããŠãããŠãã ããã evalãåé€ãããšãã¢ããªã±ãŒã·ã§ã³ã®é 延éå§ãé²æ¢ãããŸããããã¯ãã¢ããªã±ãŒã·ã§ã³ã1ïŒã©ã€ãã©ãªã«ãã£ãŠæäŸããã2ïŒéèŠãªã»ããã¢ããã³ã¹ããçºçããå Žåã«åé¡ã«ãªããŸãã evalãäžé©åã§ãããšããã»ãã¥ãªãã£ããã®ä»ã®çç±ããªãå Žåãæ¢åã®åäœããµããŒããç¶ããããšã¯ç°¡åã§ã¯ãªãã§ããããã
誰ããåæ§ã®ã±ãŒã¹ã«ééããå ŽåãPython 3.7以éã®é©åãªåé¿çã¯ããã®PEPã«åŸã£ãŠãã¢ãžã¥ãŒã«ã¬ãã«ã®__getattr__
äœæããããšã«ãããã¢ãžã¥ãŒã«ã¬ãã«ã®å€æ°ãåœé ããããšã§ãã ããã«ãããgunicorn 20.0.0ã®é倧ãªå€æŽã«ã¶ã€ããããšãªããæ æ°ãªéå§ïŒã¢ããªã±ãŒã·ã§ã³ãã¡ã¯ããªïŒãå¯èœã«ãªããŸãã
ãŸããç§ãã¡ã¯ãã®ãããªæ¯ãèããå®éã«ãµããŒãããããšã¯ãããŸãããç§ãã¡ã®ããã¥ã¡ã³ããäŸã®ã©ããããã䜿çšããŠããŸããã ããã¯ã³ãã³ãã©ã€ã³ã«é©åããŸããã
ããããããã¯æ¬åœã«é倧ãªå€æŽã§ãããäºæããªããã®ã§ãã ãã®åŸã eval
ãå
ã«æ»ããéæšå¥šã®åäœã«ã€ããŠãŠãŒã¶ãŒã«èŠåããããšã«è³æã§ãã ãã¶ãããã眮ãæããŠã人ã
ãããã¡ã¯ããªããã¶ã€ã³ãã¿ãŒã³ã䜿çšã§ããããã«ããããã«ãèšå®ãè¿œå ããããšãã§ããŸã--init-args
ïŒ
gunicorn -b :8000 --init-args="arg1,arg2" app:factory_method
ãŸãã¯ããã®ãããªãã®ã èãïŒ
@benoitcæ瀺çãªã³ãã³ãã©ã€ã³ãã©ã°ã§ãã¡ã¯ããªã¡ãœããããµããŒãããã®ã¯çŽ æŽãããã§ãããðå€å次ã®ãããªãã®ã§ãïŒ
$ gunicorn -b :8000 \
--callable \
--callable-arg "abc" \
--callable-arg "xyz" \
--callable-kwarg "key" "value" \
app:factory_method
ïŒãŸãã¯ã --factory
ãªã©ã®å¥ã®ããŒã¹åïŒ
ãã¹ããç°¡åã«å®è¡ããæ¹æ³ããªããªã£ãããããã®å€æŽã§åé¡ãçºçããŸããã ïŒiïŒç§ã®ã¢ããªã¯ç°å¢å€æ°ã«äŸåããŠãããããïŒiiïŒãã¹ãã³ã¬ã¯ã·ã§ã³ã¯ãã¹ãŠã®ã¢ãžã¥ãŒã«ãããŒããïŒdoctestsã®å ŽåïŒãïŒiiiïŒã€ã³ããŒãåŸãŸã§ã¢ããªã®æ§ç¯ã延æã§ããªããããé·ãæååãè¿œå ããã«ãããžã§ã¯ãããã¹ãããããšã¯ã§ããŸãããã¹ãŠã®ãã¹ãã³ãã³ãã®åã«ç°å¢å€æ°ãå ¥åãããã¹ãã«ä»¥åãããæéãããããŸãã
ç§ã¯Python3.7ã䜿çšããŠããã®ã§ãã¢ãžã¥ãŒã«ã¬ãã«ã®__getattr__
ã§ãããåé¿ã§ãããšæããŸããã3.7ããåã®å ŽåãããŠã³ã°ã¬ãŒã以å€ã«ãã®åé¡ã®è§£æ±ºçã¯ãªããšæããŸãã
ã³ãã³ãã©ã€ã³ãã©ã°ã§ãã¡ã¯ããªã¡ãœããããµããŒãããããšã§ããã®åé¡ã解決ã§ãããšæããŸãã ããããæãããªè§£æ±ºçããªãå Žåã¯ãä»ã®ææ¡ãããã ããã°å¹žãã§ãð
@ tjwaterman99ãããšããã®æ¹æ³ã§ã¢ããªã«åŒæ°ãæž¡ãã®ã奜ããã©ããã¯
Flaskã®äœ¿çšæ³ã®ç§ãã¡èªèº«ã®
ç§ã¯åæããŸããç°å¢ãä»ããŠåŒæ°ãæž¡ãæ¹ãçŽæçã§ããããŠãŒã¶ãŒãæ§æã1ãæã«ãŸãšããããšã奚å±ãããšæããŸãã ãã ããåŒã³åºãå¯èœãªãªããžã§ã¯ã/ãã¡ã¯ããªããµããŒãããããšã¯ãå°ãªããšãFlaskã«ãšã£ãŠããããŠããããä»ã®ãã¬ãŒã ã¯ãŒã¯ã«ãšã£ãŠãéèŠã§ãã
exec
次ã®ãªãªãŒã¹ãå»æ¢ããåã«ãèŠåãçºããå·¥å Žã§Gunicornã䜿çšããæ¹æ³ã説æããããã®+1ã
ãããèµ·ãã£ãã®ã¯æ®å¿µã§ãã å¿çæ¹æ³ã«ã¯2ã€ã®éžæè¢ããããŸãã åäœãå ã«æ»ãããšããå šå¡ã®ç§»è¡ãæ¯æŽããããšãã§ããŸãã
åäœãå ã«æ»ãå Žåã¯ãPyPIãããªãªãŒã¹ããã«ããã®ãçã«ããªã£ãŠãããããããŸããããããã¯ããŸãã«ã培åºçã ãšæããŸãã Gunicornã¯ããã®äœ¿çšæ³ãææžåãŸãã¯ææ¡ããããšã¯ãããŸããã
ãããã£ãŠãçæ§ã®ãäžäŸ¿ããè©«ã³ç³ãäžããŸãã
ããã¥ã¡ã³ããæŽæ°ããã«ã¯ãPRã§Flaskã«é£çµ¡ããå¿ èŠããããŸãã ç§ã¯ãããããŠããããã§ãã ä»ã®äººã¯ãã§ã«ããã§ç§»è¡ãã¹ãææžåããŠãããšæããŸãã
ã¢ããªã±ãŒã·ã§ã³ãã¡ã¯ããªãã€ã³ããŒãããåŒã³åºãããšã¯ã¹ããŒããã_separate_ã¢ãžã¥ãŒã«ãŸãã¯ã¹ã¯ãªããããããšäŸ¿å©ãªå Žåããããšããææ¡ãè¿œå ããŸãã ããã¯Gunicornã®ãšã³ããªãã€ã³ããšããŠæ©èœããdoctestããã®ä»ã®ããŒã«ããçç¥ããŠãéçºäžã«ãããã®ããŒã«ãå®è¡ãããšãã«äžèŠãªã€ã³ããŒããããªã¬ãŒãããªãããã«ããããšãã§ããŸãã __main__.py
ãŸãã¯web.py
ã¹ã¯ãªããã®ãããªãã®ãããã«äœ¿çšã§ããŸãã
å°æ¥çã«ã¯ããªãªãŒã¹ãå®å šã§ãããšèããããå Žåã§ãããªãªãŒã¹åè£ãå©çšã§ããããã«ããå¿ èŠããããŸãã ããããªãªãŒã¹åè£ã§æããŠããªãªãŒã¹ããŒãã®é倧ãªå€æŽãææžåãããããµã€ã¯ã«ã§éæšå¥šã«ããæ©äŒããã£ãå¯èœæ§ããããŸãã
ã³ãã³ãã©ã€ã³ã§åæååŒæ°ã®ãµããŒããè¿œå ããããšã¯æå³ããªããšæããŸãã ãã®ãªãªãŒã¹ã«ã¯é ãããŸãã é«åºŠãªãŠãŒã¹ã±ãŒã¹åãã®ã«ã¹ã¿ã ã¢ããªã±ãŒã·ã§ã³ã¯ãã§ã«ãµããŒããããŠããŸãã ãŸããå€ãã®ãã¬ãŒã ã¯ãŒã¯ã«ã¯ãã¢ããªã±ãŒã·ã§ã³ã«èšå®ãæž¡ãããã®ç¬èªã®æšå¥šæ¹æ³ããããŸãã Gunicornã¯ç¬èªã®ãã®ãæäŸããå¿ èŠã¯ãããŸããã ãã®åé¡ãä¿®æ£ããããã«åŒæ°ãè¿œå ããããšãããšãå°æ¥ãã®çš®ã®é倧ãªå€æŽã®è¡šé¢ç©ãæ¡å€§ããŸãã Gunicornã®CLIãµãŒãã§ã¹ãå¯èœãªéãæå°åããããšãç®æãå¿ èŠããããŸãã
ããã¥ã¡ã³ããæŽæ°ããã«ã¯ãPRã§Flaskã«é£çµ¡ããå¿ èŠããããŸãã ç§ã¯ãããããŠããããã§ãã ä»ã®äººã¯ãã§ã«ããã§ç§»è¡ãã¹ãææžåããŠãããšæããŸãã
@ bilalshaikh42ã¯ãã§ã«https://github.com/pallets/flask/pull/3421ã§ãããè¡ã£ãŠããããã§ã
ïŒããã®Flaskã¡ã³ããã®1人ïŒ
ããã§eval
ãåãé€ãããšã«å®å
šã«åæããŸãããã¢ããªãã¡ã¯ããªãæ瀺çã«ãµããŒãããå¿
èŠããããšæããŸãã ã¢ããªãã¡ã¯ããªã®èŠç¹ã¯ãã€ã³ããŒãå¯èœãªapp
ãªããžã§ã¯ãã䜿çšããªãããã«ããããšã§ãïŒããã䜿çšãããšã埪ç°äŸåé¢ä¿å°çãçºçããããšãå€ãããïŒã
flask run
cliïŒéçºçšã®ã¿ïŒã§ã¯ãã¢ããªãã¡ã¯ããªãéåžžã«äžè¬çã§ãããããå®éã«æ瀺çãªãµããŒããè¿œå ããŸããã
ãã¡ããã wsgi.py
ãå«ãfrom myapp. import make_app; app = make_app()
wsgi.py
ãäœæããã®ã¯ç°¡åã§ãã ããããç§ã¯ãã®ãã¡ã€ã«ãåå¥ã«ç¶æããå¿
èŠããããŸãïŒããã¯ã pip install myapp
ãå®è¡ããããã«å¿
èŠãªãã¹ãŠãã€ã³ã¹ããŒã«ããªããããäžäŸ¿ã§ãïŒããŸãã¯ããã±ãŒãžã«å
¥ããïŒã€ãŸããå
éšããã€ã³ããŒãã§ããããã«ãªããŸããïŒãééã£ãŠããã¢ããªèªäœïŒ
Flaskã§ã¯ãåŒã³åºãå¯èœãªã¢ããªãã¡ã¯ããªããã§ãã¯ãã eval
é Œããã«åŒã³åºãæ瀺çãªæ¹æ³ãæ¡çšããŸãããããããããã®ãããªãã®ãæ€èšã§ããŸããïŒ éæ³ãæžããããå Žåã¯ãã¢ããªã±ãŒã·ã§ã³ãæããããšã¢ããªãã¡ã¯ããªãæãããã«ç°ãªãCLIåŒæ°ã䜿çšããããšãã§ããŸãã
å°æ¥çã«ã¯ããªãªãŒã¹ãå®å šã§ãããšèããããå Žåã§ãããªãªãŒã¹åè£ãå©çšã§ããããã«ããå¿ èŠããããŸãã ããããªãªãŒã¹åè£ã§æããŠããªãªãŒã¹ããŒãã®é倧ãªå€æŽãææžåãããããµã€ã¯ã«ã§éæšå¥šã«ããæ©äŒããã£ãå¯èœæ§ããããŸãã
RCãæ¬åœã«åœ¹ç«ã€ãã©ããã¯ããããŸãã-äžè¬çã«äººã
ã¯--pre
ã€ã³ã¹ããŒã«/ã¢ããã°ã¬ãŒãããŸããïŒãããã©ãã»ã©ã²ã©ãæ©èœããããã§ããããŸã-ããã¯æ瀺çã«æå®ãããããã±ãŒãžã ãã§ãªããã©ããªã«æ·±ããŠããã¹ãŠã®ãã¹ããããäŸåé¢ä¿ã«åœ±é¿ããŸãããããã£ãŠãäŸåé¢ä¿ã®äŸåé¢ä¿ã«ãã£ãŠå£ãããã¬ãªãªãŒã¹ãåã蟌ãŸããã®ã¯éåžžã«ç°¡åã§ãïŒããã®ãããããŒãžã§ã³ãåºå®ããªãã£ã人ã¯ãå®éã«ãªãªãŒã¹ããããŸã§ç ŽæãèŠã€ããããšã¯ãããŸããã
zope.hookable
ã¯ãïŒãªãã·ã§ã³ã®Cæ¡åŒµæ©èœã«ããïŒæ¬è³ªçã«ãªãŒããŒãããã®ãªããæ æ°ãªãã¡ã¯ããªã®ãããªã¢ãããŒããå®è£
ããç°¡åãªæ¹æ³ãæäŸããŸãã ãã ããè¿œå ã®åŒæ°ãæž¡ãããšã«ã€ããŠã¯äœãããŸããã
# app.py
from zope.hookable import hookable
def make_app():
def _(environ, start_response, value=b'Hello'):
start_response('200 OK',
[('Content-Type', 'text/plain')])
return [value]
return _
<strong i="8">@hookable</strong>
def app(environ, start_response):
real_app = make_app()
app.sethook(real_app)
return real_app(environ, start_response, b"First time")
$ gunicorn app:app
[2019-11-12 05:53:47 -0600] [12457] [INFO] Starting gunicorn 20.0.0
[2019-11-12 05:53:47 -0600] [12457] [INFO] Listening at: http://127.0.0.1:8000 (12457)
[2019-11-12 05:53:47 -0600] [12457] [INFO] Using worker: sync
[2019-11-12 05:53:47 -0600] [12460] [INFO] Booting worker with pid: 12460
...
% http localhost:8000
HTTP/1.1 200 OK
Connection: close
Content-Type: text/plain
Date: Tue, 12 Nov 2019 11:53:49 GMT
Server: gunicorn/20.0.0
Transfer-Encoding: chunked
First time
% http localhost:8000
HTTP/1.1 200 OK
Connection: close
Content-Type: text/plain
Date: Tue, 12 Nov 2019 11:53:51 GMT
Server: gunicorn/20.0.0
Transfer-Encoding: chunked
Hello
ãã¡ããã
wsgi.py
ãå«ãfrom myapp. import make_app; app = make_app()
wsgi.py
ãäœæããã®ã¯ç°¡åã§ãã ããããç§ã¯ãã®ãã¡ã€ã«ãåå¥ã«ç¶æããå¿ èŠããããŸãïŒããã¯ãpip install myapp
ãå®è¡ããããã«å¿ èŠãªãã¹ãŠãã€ã³ã¹ããŒã«ããªããããäžäŸ¿ã§ãïŒããŸãã¯ããã±ãŒãžã«å ¥ããïŒã€ãŸããå éšããã€ã³ããŒãã§ããããã«ãªããŸããïŒãééã£ãŠããã¢ããªèªäœïŒ
ãããžã§ã¯ãã®wsgi.py
ãééã£ãŠãããã1ã€ã®çç±ã¯ããããžã§ã¯ãå
ã®ãã¹ãŠã®ã¢ãžã¥ãŒã«ãã€ã³ããŒãããããŒã«ãããããšã§ãã äŸãã°ã pytestã¯ãdoctestãæ¢ããšãã«å®è¡ããŸãã
ããã«å¥ã®ãã©ã¹ã³ã¡ã³ããã @ThiefMasterã¯ç§ãèšãããããšããã¹ãŠèšã£ãã®ã§ãç§ã¯äž»ã«ãã®æ©èœã®ãµããŒããç¹°ãè¿ããŠããŸãã
ç§ã¯åãæãã«åæeval
ããšç§ã¯ãããåé¿flask run
ã 以åã®åäœã®ããå¶çŽãããããŒãžã§ã³ãè¿œå ã§ããŸãã ã³ãã³ãã©ã€ã³ãªãã·ã§ã³ã«èŠªãå«ãŸããŠããå Žåã¯ãå®éã®ã¢ããªãè¿ããã¡ã¯ããªã§ãããšæ³å®ããŸãã literal_eval
ã䜿çšããŠèŠªã®å
容ã解æããŠããã解æããããã©ã¡ãŒã¿ãŒã䜿çšããŠãã¡ã¯ããªãåŒã³åºããŸãã
wsgi.py
ãã¡ã€ã«ã®ãªããã¡ã¯ããªãã¿ãŒã³ã¯ããªã䟡å€ããããšæããŸãã Gunicornã§ãããç¶æããæ¹æ³ãèŠã€ããã®ãæäŒãããã§ãã
å·¥å Žã®ãããªã¢ããªã±ãŒã·ã§ã³æååã®literal_eval
ã®PRããŸãšããããšæããŸãããïŒ ããã¯gunicorn.util.import_app
ãŸãã
ãã¹ããè¿œå ããå¿ èŠããããŸãããGunicornã«ç§»æ€ãããFlaskã®ã³ãŒãã¯æ¬¡ã®ãšããã§ãïŒ https ïŒ//github.com/benoitc/gunicorn/compare/master ... davidismïŒimport-factory
@davidismèå³ãããå Žåã¯ãã¢ããªãã¡ã¯ããªããã¢ããªãèªã¿èŸŒãã®ã«åœ¹ç«ã€å¯èœæ§ã®ããé¢æ°ãast.parse
ãšast.literal_eval
ã䜿çšããŠè©äŸ¡ãããããã eval
åŒã³åºãã¯ãããŸããã
import ast
from types import ModuleType
from typing import Any
def get_app(module: ModuleType, obj: str) -> Any:
"""
Get the app referenced by ``obj`` from the given ``module``.
Supports either direct named references or app factories, using `ast.literal_eval` for safety.
Example usage::
>>> import collections
>>> get_app(collections, 'Counter')
<class 'collections.Counter'>
>>> get_app(collections, 'Counter()')
Counter()
>>> get_app(collections, 'import evil_module') # doctest: +ELLIPSIS
Traceback (most recent call last):
...
ValueError: Could not parse 'import evil_module' as a reference to a module attribute or app factory.
>>> get_app(collections, '(lambda: sys.do_evil)()')
Traceback (most recent call last):
...
ValueError: App factories must be referenced by a simple function name
>>> get_app(collections, '(1, 2, 3)')
Traceback (most recent call last):
...
ValueError: Could not parse '(1, 2, 3)' as a reference to a module attribute or app factory.
"""
# Parse `obj` to an AST expression, handling syntax errors with an informative error
try:
# Note that mode='eval' only means that a single expression should be parsed
# It does not mean that `ast.parse` actually evaluates `obj`
expression = ast.parse(obj, mode='eval').body
except SyntaxError as syntax_error:
raise ValueError("Could not parse '{}' as a reference to a module attribute or app factory.".format(obj)) from syntax_error
# Handle expressions that just reference a module attribute by name
if isinstance(expression, ast.Name):
# Expression is just a name, attempt to get the attribute from the module
return getattr(module, expression.id)
# Handle expressions that make a function call (factory)
if isinstance(expression, ast.Call):
# Make sure the function name is just a name reference
if not isinstance(expression.func, ast.Name):
raise ValueError("App factories must be referenced by a simple function name")
# Extract the function name, args and kwargs from the call
try:
name = expression.func.id
args = [ast.literal_eval(arg) for arg in expression.args]
kwargs = {keyword.arg: ast.literal_eval(keyword.value) for keyword in expression.keywords}
except ValueError as value_error:
raise ValueError("Could not evaluate factory arguments, please ensure that arguments include only literals.") from value_error
# Get and call the function, passing in the given arguments:
return getattr(module, name)(*args, **kwargs)
# Raise an error, we only support named references and factory methods
raise ValueError("Could not parse '{}' as a reference to a module attribute or app factory.".format(obj))
åŒæ°ãªãã®ã¢ããªãã¡ã¯ããªã®ã¿ããµããŒãããããšã決å®ããå Žåã¯ããã¹ãŠã®åŒæ°åŠçã³ãŒããåé€ã§ããŸãã ååãšãã¡ã¯ããªåŒã³åºããå®å šã«åºå¥ããããšã¯äŸç¶ãšããŠããŸãæ©èœããŸãïŒãããŠããŠãŒã¶ãŒããã¡ã¯ããªã«åŒæ°ãæž¡ãããšãããšãã«ç¹å®ã®ãšã©ãŒã¡ãã»ãŒãžããŠãŒã¶ãŒã«äžããã®ã«åœ¹ç«ã¡ãŸãïŒ
@ThiefMasterç§ã¯ãŸã ãã®ãããªãã¿ãŒã³ããµããŒããã¹ãã ãšç¢ºä¿¡ããŠããŸããã ããã¯ã©ã®ããã«åœ¹ç«ã¡ãŸããïŒ æ¬åœã«å¿ èŠãªå Žåã¯ãç°å¢å€æ°ã䜿çšããŠã«ã¹ã¿ã åŒæ°ãŸãã¯æ§æãæž¡ããªãã®ã¯ãªãã§ããïŒ
確ãã«ãmyappãããå«ãwsgi.pyãäœæããŸãã make_appãã€ã³ããŒãããŸãã app = make_appïŒïŒã¯ç°¡åã§ãã ããããç§ã¯ãã®ãã¡ã€ã«ãåå¥ã«ç¶æããå¿ èŠããããŸãïŒããã¯ãpip install myappãå®è¡ã«å¿ èŠãªãã¹ãŠãã€ã³ã¹ããŒã«ããªããããäžäŸ¿ã§ãïŒããŸãã¯ããã±ãŒãžã«å ¥ããïŒã€ãŸããã¢ããªèªäœããã€ã³ããŒãã§ããããã«ãªããŸããïŒãããã¯ééã£ãŠããã§ãããïŒ
ç§ã¯ãããç解ããŠããŸããããªããã®ãããªãã¡ã€ã«ãå¥ã ã«ç¶æããªããã°ãªããªãã®ã§ããïŒ
ããã±ãŒãžã«å«ãŸããŠããå Žåã¯ãã€ã³ããŒãå¯èœã§ãã ãããã£ãŠããã倧ããªãããžã§ã¯ããããå Žåã誰ããcurrent_app
ãªã©ã䜿çšãã代ããã«ãæçµçã«ã¯ãããã€ã³ããŒãããã ãã§ããã€ãŸãããã®çš®ã®ééããå«ãPRãåŠçããå Žåã¯ãããå€ãã®äœæ¥ãå¿
èŠã«ãªããŸãã
ããã±ãŒãžã®å€ã«ããå Žåã pip install
å®è¡ããŠãååŸã§ããŸããã
FWIWãç§ã¯åŒæ°ãæž¡ãããšãæ¬åœã«æ°ã«ããŸããã éåžžããããã¯å¿ èŠãããŸããïŒenv varsã¯ç¢ºãã«è¡ãæ¹æ³ã§ãïŒã ããããå°ãªããšãã¢ããªãªããžã§ã¯ãã®ä»£ããã«åŒã³åºãå¯èœãªã¢ããªãã¡ã¯ããªãæãããšãã§ããããšã¯éåžžã«äŸ¿å©ã§ãïŒ
æ¬åœã«å¿ èŠãªå Žåã¯ãç°å¢å€æ°ã䜿çšããŠã«ã¹ã¿ã åŒæ°ãŸãã¯æ§æãæž¡ããªãã®ã¯ãªãã§ããïŒ
pytest
ã¯ããã¹ããèŠã€ããããã«ãããžã§ã¯ãå
ã®ãã¹ãŠã®ã¢ãžã¥ãŒã«ãããŒãããŸãã ç°å¢å€æ°ãŸãã¯æ§æãã¡ã€ã«ã«äŸåããã°ããŒãã«app=Flask()
ãªããžã§ã¯ããããå Žåããã®ãªããžã§ã¯ãã¯ãã¹ãã®å®è¡æã«ããŒããããŸãã ç°å¢å€æ°ãæ§æãã¡ã€ã«ãèšå®ããã«ãã¹ããå®è¡ã§ãããšäŸ¿å©ã§ãã ã¢ããªã®ãã¡ã¯ããªãã¿ãŒã³ã¯ããã«æé©ã§ãã
ããã€ãã®äººæ°ã®ããFlaskãã¥ãŒããªã¢ã«ããããããåŒæ°ãã¿ãŒã³ã®ãããã¡ã¯ããªã¯ããäžè¬çã§ãããã®ããã flask run
ãµããŒãããŸããã ç§ã¯ãç°å¢ã䜿çšããŠã¢ããªãæ§æããããšãæãŸããããšã«åæããŸãããã®ãããåŒæ°ãªãã§ãã¡ã¯ããªã®åŒã³åºãããµããŒããããããçž®å°ãããããŒãžã§ã³ã§åé¡ãããŸããã
# an app is a name only
$ gunicorn module:app
# a factory has (), but no args allowed
$ gunicorn module:factory()
@tilgoviåæããŸãã ç§ã®äž»ãªåé¡ã¯ãããã誰ããå£ããšã¯æã£ãŠããªãã£ããšããããšã§ããããããããªãç§ã¯è©äŸ¡ïŒãŸãã¯ããå®å
šãªãã®ïŒãå
ã«æ»ããŠéæšå¥šã«ããããšãææ¡ããŸããã äžæ¹ãã¯ãããã®åäœã¯ãµããŒããããŠãããã eval
ã䜿çšããããšã«ããäžå¹žãªåœ±é¿
@davidismãããããã ããããåŒã³åºãå¯èœãªãªããžã§ã¯ããã¢ããªã±ãŒã·ã§ã³ãšããŠäœ¿çšããã®ãšã©ãéãã®ã§ããããã
ã©ãããæå³ãããããŸãããããã£ãšå ·äœçãªäŸãæããŠããã ããŸããïŒ ãã¡ã¯ããªã¯WSGIã¢ããªã±ãŒã·ã§ã³ãè¿ããŸãããããèªäœã¯WSGIã¢ããªã±ãŒã·ã§ã³ã§ã¯ãããŸããã
@davidismç§ã¯äœããããã
def make_app():
from mymodule.application import MainApp
return MainApp()
application = make_app()
次ã«èª°ããgunicorn -b :8000 somemodule:application
å®è¡ããŸã
ããã«ãããã³ãŒããã€ã³ããŒããããšãã«application
ãåžžã«è©äŸ¡ããããã¡ã¯ããªã®ç®çãæãªãããŸãã
WSGIåŒã³åºãå¯èœãªããžã§ã¯ãã¯ã¯ã©ã¹ã€ã³ã¹ã¿ã³ã¹ã«ããããšãã§ããããããããããããæå³ããããã®ã§ãã
class Application:
_app = None
def __call__(self, environ, start_response):
if self._app is None:
from wherever import make_app
self._app = make_app()
return self._app(environ, start_response)
application = Application()
ïŒ zope.hookable
äŸã¯åºæ¬çã«åãã§ãããå®åžžç¶æ
ã§ã®ãªãŒããŒããããå°ãªããªããŸããïŒ
å®éã®WSGIã¢ããªãäœæããWSGIã¢ããªãæã€ããšã¯çæ³çã§ã¯ãããŸããã ããã¯ãå®éã®ãšã³ããªãã€ã³ãã«ãããã·ãããã¹ãŠã®ãªã¯ãšã¹ãã«å¯Ÿããè¿œå ã®é¢æ°åŒã³åºãã«ãªããŸããã ã»ããã¢ããã¯æåã®ãªã¯ãšã¹ãã®åã«è¡ãå¿ èŠããããŸãããçŸåšã¯ãããŸã§å»¶æãããŠãããããæåã®ãªã¯ãšã¹ãã«ã¯ïŒå Žåã«ãã£ãŠã¯ããªãïŒæéãããããŸãã
ããã§åé¡ãšãªã£ãŠããæ©èœã¯ãã©ã³ã¿ã€ã /ç°å¢æ§æã«åºã¥ããŠãã®ãªããžã§ã¯ããäœæãããã¡ã¯ããªé¢æ°ã§ããããã¯ãã¢ããªã±ãŒã·ã§ã³ããŒããåé¢ãã埪ç°ã€ã³ããŒããåé¿ãããã¹ãã®åé¢ã容æã«ããã®ã«åœ¹ç«ã¡ãŸãã ãã¡ã¯ããªãæ瀺çã«åŒã³åºãã³ãŒãã®ã©ããã«ããã«ãããªã³ã°ã®ç®çãç¡å¹ã«ãªããŸãããŠãŒã¶ãŒã代ããã«Flaskã§äœ¿çšã§ããæ©èœã䜿çšããå¿ èŠããããšãã«ããããããã®ã¢ããªãªããžã§ã¯ããä»ããã€ã³ããŒãããå¿ èŠãããããšæãããšãä¿èšŒããŸãã
ãã®æç¹ã§ãç§ãã¡ãæ±ããŠããã®ã¯ããã€ã³ããŒãæååãparensã§çµãã£ãŠããå Žåã¯ãã€ã³ããŒããããååãåŒã³åºããŠã¢ããªãååŸãããããšã ãã§ãã
ãããåé¿ããæ¹æ³ã¯ãããããããšæããŸãããããã¯åã«ç§ãã¡ãã¿ãŒã²ãããªãŒãã£ãšã³ã¹ã§ã¯ãªãããšãæå³ããŸãã ããã±ãŒãžã®å€éšã«ããã¹ã¯ãªãããã³ã³ããã®ãšã³ããªãã€ã³ããšããŠåºè·ãããããããç¡èŠããããã«pytestãæ§æããããããªã©ã®ããšãã§ããããšã¯ç¥ã£ãŠããŸããããã¥ãŒããªã¢ã«ããã©ããŒããŠããå¯èœæ§ã®ããããããå£ãã人ãæ°ã«ãããããšæããŸãããã¬ãŒã¹ããã¯ãç解ããŠããŸããã
éåžžã«éå®ããããã¢ããªãªããžã§ã¯ããåŒæ°ãªãã§åŒã³åºãå¯èœã§ããå Žåã¯ããã¡ã¯ããªãšããŠåŒã³åºãããã¿ãŒã³ã¯æ©èœããå¯èœæ§ããããŸãããåŒã³åºãå¯èœãªããžã§ã¯ããå®éã«ã¯è£
食ãäžååã§ãã€ã³ããã¹ãã¯ã·ã§ã³ããåŒæ°ãç°¡åã«æããã«ããªãWSGIã¢ããªã±ãŒã·ã§ã³ã§ããå Žåã¯å€±æããŸãã å¯å€§ã«ãªãããã®ãªãã eval
ãé¿ããªããããããŸã§ã®ãã¹ãŠããµããŒãããå¿
èŠãããã®ã§ããããç§ãã¡ã®ãã¹ãéã ãšæããŸãã
ç§ã¯æ¬åœã«ãã¹ãŠã®ææ¡ã«æè¬ããããã解決ããã®ãæäŒããŸããçããã
literal_eval
ã䜿çšãã@davidismãš@connorbrintonã®äž¡æ¹ã®ææ¡ã奜ãã§ãã
ããã«ãããã³ãŒããã€ã³ããŒããããšãã«ã¢ããªã±ãŒã·ã§ã³ãåžžã«è©äŸ¡ããããã¡ã¯ããªã®ç®çãæãªãããŸãã
å®è¡æã«ã¢ããªãåæåããã¯ãŒã«ãŒã䜿çšããåŒã³åºãå¯èœãªããžã§ã¯ããè¿ããŸãã ããã¯ããã»ã©éãã¯ãããŸããã
ãã®ãã¿ãŒã³ã«ã€ããŠã®ç§ã®äž»ãªäºåã¯ãHUPãŸãã¯USR2ã®æåŸ ãç Žãããšãã§ããããã€ãã®ã³ãŒããäºåã«çæããããšã人ã ã«å¥šå±ããããšã§ãã ãŸããçŸåšã®UIãå£ããŸãã gunicornã®å°æ¥ã®äœ¿çšæ³ã§æ©èœããŸããïŒ
ãšã«ãããéžæè¢ã¯æ¬¡ã®ãšããã§ãã
ïŒ1ïŒã¯é£ããããšã§ããããµããŒãããããšããªãããšãèãããšãè«ççãªãã¹ã§ããããŸãã
ïŒ2ïŒããçš®ã®çŸçæèŠããããã³ãã³ãã©ã€ã³UIãå£ããŠããŸããgunicornã§ãã¹ãããã«ã¯ãããã€ãã®ãã¹ã/äŸãå¿
èŠã§ããliteral_evalsã®ãããªãã®ã䜿çšããŠãã ããã
ïŒ2ïŒã¯ãµããŒãã§ããŸããããã¹ãããé¡ãããŸãã ãŸãããããææžåããå¿ èŠããããŸããïŒ
@tilgovi @jamadden @berkerpeksag @sirkonstããªãã®å¥œã¿ã¯äœã§ããïŒ
å¥ã®é倧ãªã§ãããããã¯literal_eval
解決ãããŸããã
ããšãã°ãPlotly Dashã§ã¯ã Dash
ãªããžã§ã¯ãã䜿çšããŸãããã®ãªããžã§ã¯ãã¯ãå
éšçã«server
å±æ§ãšããŠFlask
ã€ã³ã¹ã¿ã³ã¹ãæã£ãŠããŸãã äžéšã®äººã
ã¯äœ¿çšããŠããŸããïŒ
gunicorn "module:app.server"
ããããããããµããŒããããã¹ããã©ããã¯ããããŸããã flask run
ãããããµããŒãããŠããŸããã Dash
ãªããžã§ã¯ãã«ã¯ã __call__
ã«æž¡ãããFlask.__call__
__call__
ã¡ãœãããå¿
èŠãªããã§ãã ããã«ã Dashã®ããã¥ã¡ã³ãã§ã¯ã server = app.server
ãå®è¡ããGunicornã«ãã®ããšãææããŠãããããããã¯ã»ãšãã©ã®å Žåã誀ã£ãæ
å ±ãæž¡ãããå Žåã®ããã§ãã
@davidismä»æ¥ã¯ç æ°ã§ãããæææ¥ã®ãªãªãŒã¹ã®ããã«ããã®æ¥ææ¥ã«ããã«ã€ããŠèŠãŠãããŸãã @tilgoviããã®ææ¡ã¯é©åã§ãããäžè¬çãªèšç»ã¯ãå€ãè©äŸ¡ãå®å šãªè©äŸ¡ã«çœ®ãæããããšã§ãã
ãã®ãããªåæåã䜿çšããŠãããŠãŒã¶ãŒã«èŠåããå¿ èŠããããšæããŸãã èãïŒ cc @tilgovi
å¥ã®å®è£ ãäœæãããå Žåãé€ããŠãäžèšã§ãªã³ã¯ãããã©ã³ããåææ¥ã«ãã¹ããè¡ãPRã«å€æããããšããŸãã
@davidismå ã«é²ã¿ãŸãã ç§ã¯æ¥ææ¥ã«æ»ã£ãŠããŠãå¿ èŠã«å¿ããŠã¬ãã¥ãŒããŸã:)ããããšãïŒ
å°ãé ããŠãä»ããã«åãçµãã§ããŸãã
@connorbrinton ast.parse
ã䜿çšãããšããã¯ãŒã«ãªã¢ã€ãã¢ã§ããè©ŠããŠã¿ãŠãäžç·ã«è¡ãå Žåã¯ãã³ãããã®å
±èè
ãšããŠããªããå«ããŸãã
ãã人æ°ã®ããïŒãããŠããªãå€ãïŒStack Overflowã®åçãããããŠãŒã¶ãŒãv19ã®åäœã«èªå°ããŠãããšããç¹ã§ãã£ã€ã ã鳎ããããã£ãã ãã§ããããã¯ãéžæå 容ã«ãã£ãŠã¯æŽæ°ãå¿ èŠã«ãªãå ŽåããããŸãïŒ 8495367 / using-additional-command-line-arguments-with-gunicorn
ãã¹ã¿ãŒã§ä¿®æ£ãããŸããã ããããããããšã@davidism ïŒ
åŠçããããã¹ãŠã®ã±ãŒã¹ã¯ãã®ãã¹ãã«å«ãŸããŸãïŒ https ïŒ
æãåèã«ãªãã³ã¡ã³ã
ãã¹ã¿ãŒã§ä¿®æ£ãããŸããã ããããããããšã@davidism ïŒ
åŠçããããã¹ãŠã®ã±ãŒã¹ã¯ãã®ãã¹ãã«å«ãŸããŸãïŒ https ïŒ