λ΄ λ²μ μ 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 λ€μ λκ΄νΈκ° μ΄μ μ¬λΌμ‘μ΅λλ€.) μ²μμλ λͺ¨λ κ²μ΄ μ 보μ
λλ€.
μΉμ¬μ΄νΈ_1 | [2019-11-10 19:18:54 +0000] [1] [INFO] κΈ°λμ½ 20.0.0 μμ
μΉμ¬μ΄νΈ_1 | [2019-11-10 19:18:54 +0000] [1] [INFO] λ£κΈ°: http://0.0.0.0 :8000 (1)
μΉμ¬μ΄νΈ_1 | [2019-11-10 19:18:54 +0000] [1] [INFO] μμ μ μ¬μ©: λκΈ°ν
μΉμ¬μ΄νΈ_1 | [2019-11-10 19:18:54 +0000] [11] [INFO] pid: 11λ‘ μμ μ λΆν
κ·Έλ¬λ νλΌμ€ν¬ μΉ μ¬μ΄νΈ/μλν¬μΈνΈλ₯Ό λ‘λνλ €κ³ νλ©΄ λ€μκ³Ό κ°μ΄ νμλκΈ° λλ¬Έμ μ κΈ°λ£¨μΌ λΏμ λλ€.
[2019-11-10 19:20:28 +0000] [11] [ERROR] μμ² μ²λ¦¬ μ€λ₯ /
μΉμ¬μ΄νΈ_1 | μμΆμ (κ°μ₯ μ΅κ·Ό νΈμΆ λ§μ§λ§):
μΉμ¬μ΄νΈ_1 | νμΌ "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", 134ν, νΈλ€
μΉμ¬μ΄νΈ_1 | self.handle_request(μμ κΈ°, μμ², ν΄λΌμ΄μΈνΈ, μ£Όμ)
μΉμ¬μ΄νΈ_1 | handle_requestμ νμΌ "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", 175ν
μΉμ¬μ΄νΈ_1 | respiter = self.wsgi(νκ²½, resp.start_response)
μΉμ¬μ΄νΈ_1 | TypeError: create_app()μ 0κ°μ μμΉ μΈμλ₯Ό μ¬μ©νμ§λ§ 2κ°κ° μ£Όμ΄μ‘μ΅λλ€.
μ΄κ²μ λΆλͺ ν gunicorn λ²μ 20.0.0μ λ¬Έμ μ λλ€.
: κ·Έκ²μμ΄ λ³κ²½μ κ΄λ ¨λμ΄μΌνλ€ https://github.com/benoitc/gunicorn/commit/3701ad9f26a7a4c0a081dfd0f6e97ecb272de515#diff # 2043λ₯Ό ν΅ν΄ -0b90f794c3e9742c45bf484505e3db8dR377.
κ·νμ μΈ‘λ©΄μ λ¬Έμ λ₯Ό ν΄κ²°νλ ν κ°μ§ λ°©λ²μ λ΄ λ³΄λΈ κ²μ
λλ€ myapp = create_app()
κΈ°λ³Έ λͺ¨λλ‘ μ΄ μμμ app:myapp
. μ΄κ²μ μλν΄μΌ ν©λλ€. μλνμ§ μμΌλ©΄ μλ €μ£ΌμΈμ.
κ±°κΈ°μμ ν΄μΌ ν μΌμ΄ μλμ§ μ΄ν΄λ³΄κ² μ΅λλ€. @berkerpeksag μ κ±°κΈ°μμ eval
κ° νμνμ΅λκΉ?
μ΄ λ³κ²½ μ¬νκ³Ό κ΄λ ¨λμ΄μΌ ν©λλ€: 3701ad9#diff-0b90f794c3e9742c45bf484505e3db8dR377 via #2043 .
κ·νμ μΈ‘λ©΄μ λ¬Έμ λ₯Ό ν΄κ²°νλ ν κ°μ§ λ°©λ²μ λ΄ λ³΄λΈ κ²μ λλ€
myapp = create_app()
κΈ°λ³Έ λͺ¨λλ‘ μ΄ μμμ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 κΈμ, λλ μ΄λ° μμΌλ‘ μ±μ μΈμλ₯Ό μ λ¬νλ κ²μ μ’μνλμ§ νμ νμ§ λͺ»ν©λλ€. μ’μ μκ°μ΄ μλ κ² κ°μμ. μΈμλ envλ₯Ό ν΅ν΄ μ λ¬λμ΄μΌ ν©λλ€.
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 νλ μμν¬(νλΌμ€ν¬) μ€ νλμ λν κ³΅κ° λ¬Έμμ μΈκΈλ λͺ μμ μΈ λμμ΄λ©° μ΄μ μ νλ‘μ νΈμμ μ§μν κ²μμ κΈ°μ΅νμμμ€. evalμ μ κ±°νλ©΄ μμ© νλ‘κ·Έλ¨μ μ§μ° μμμ΄ λ°©μ§λ©λλ€. μ΄λ μμ© νλ‘κ·Έλ¨μ΄ 1) λΌμ΄λΈλ¬λ¦¬μμ μ 곡λκ³ 2) μ¬μν μ€μ λΉμ©μ΄ λ°μνλ κ²½μ° λ¬Έμ μ λλ€. νκ°κ° λΆμ μ ν 보μμ΄λ λ€λ₯Έ μ΄μ κ° μλ€λ©΄ κΈ°μ‘΄ νλμ κ³μ μ§μνλ κ²μ΄ λ κ°λ¨νμ§ μμκΉμ?
λκ΅°κ° λΉμ·ν κ²½μ°μ μ§λ©΄νλ κ²½μ° 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) ν μ€νΈ 컬λ μ μ΄ λͺ¨λ λͺ¨λ(doctestμ©)μ λ‘λνκ³ (iii) κ°μ Έμ€κΈ°κ° λλ λκΉμ§ λ μ΄μ μ± κ΅¬μ±μ μ°κΈ°ν μ μκΈ° λλ¬Έμ κΈ΄ λ¬Έμμ΄μ μΆκ°νμ§ μκ³ λ λ΄ νλ‘μ νΈλ₯Ό ν μ€νΈν μ μμ΅λλ€. λͺ¨λ ν μ€νΈ λͺ λ Ή μ μ νκ²½ λ³μλ₯Ό μ¬μ©νκ³ ν μ€νΈλ μ΄μ λ³΄λ€ λ μ€λ 걸립λλ€.
μ λ Python 3.7μ μ¬μ©νκ³ μκΈ° λλ¬Έμ λͺ¨λ μμ€μ __getattr__
λ‘ μ΄ λ¬Έμ λ₯Ό ν΄κ²°ν μ μλ€κ³ μκ°νμ§λ§ 3.7 μ΄μ λ²μ μ κ²½μ° λ€μ΄κ·Έλ μ΄λ μΈμ μ΄ λ¬Έμ μ λν ν΄κ²°μ±
μ΄ μλ€κ³ μκ°ν©λλ€.
λͺ λ Ήμ€ νλκ·Έλ‘ ν©ν 리 λ©μλλ₯Ό μ§μνλ©΄ μ΄ λ¬Έμ κ° ν΄κ²°λ κ²μ΄λΌκ³ μκ°ν©λλ€. κ·Έλλ νμ€ν ν΄κ²°μ± μ΄ μλ€λ©΄ λ€λ₯Έ μ μλ κ°μ¬νκ² μ΅λλ€ π
@ tjwaterman99 κΈμ, λλ μ΄λ° μμΌλ‘ μ±μ μΈμλ₯Ό μ λ¬νλ κ²μ μ’μνλμ§ νμ νμ§ λͺ»ν©λλ€. μ’μ μκ°μ΄ μλ κ² κ°μμ. μΈμλ envλ₯Ό ν΅ν΄ μ λ¬λμ΄μΌ ν©λλ€.
Flask μ¬μ© μ λν μ°λ¦¬ μμ μ
λμν©λλ€. νκ²½μ ν΅ν΄ μΈμλ₯Ό μ λ¬νλ κ²μ΄ λ μ§κ΄μ μ΄κ³ μ¬μ©μκ° ν κ³³μμ ꡬμ±μ μ μ§νλλ‘ κΆμ₯ν©λλ€. κ·Έλ¬λ νΈμΆ κ°λ₯ν κ°μ²΄/ν©ν 리λ₯Ό μ§μνλ κ²μ μ μ΄λ Flask λ° μλ§λ λ€λ₯Έ νλ μμν¬μμλ μ€μν©λλ€.
exec
λ€μ 릴리μ€λ₯Ό λ μ΄μ μ¬μ©νμ§ μκΈ° μ μ κ²½κ³ λ₯Ό νμνκ³ κ³΅μ₯μμ Gunicornμ μ¬μ©νλ λ°©λ²μ λν μ§μΉ¨μ μ 곡νλ©΄ +1μ
λλ€.
μ΄λ° μΌμ΄ μΌμ΄λ κ²μ λΆνν μΌμ λλ€. μ°λ¦¬λ μλ΅ λ°©λ²μ λν΄ λ κ°μ§ μ νμ΄ μμ΅λλ€. νλμ λ€μ λ°κΎΈκ±°λ λͺ¨λ μ¬λμ΄ μ΄μ£Όνλλ‘ λμΈ μ μμ΅λλ€.
μ°λ¦¬κ° λμμ λ€μ λ³κ²½νλ€λ©΄ 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 κ΄λ¦¬μ μ€ ν λͺ )
κ±°κΈ°μμ eval
μμ λ λ° μ μ μΌλ‘ λμνμ§λ§ μ± ν©ν 리μ λν λͺ
μμ μΈ μ§μμ΄ μμ΄μΌ νλ€κ³ μκ°ν©λλ€! μ± ν©ν 리μ μμ μ κ°μ Έμ¬ μ μλ app
κ°μ²΄λ₯Ό μ¬μ©νμ§ μλ κ²μ
λλ€(μ΄λ₯Ό μ¬μ©νλ©΄ μν μ’
μμ± μ§μ₯μ΄ μμ£Ό λ°μνκΈ° λλ¬Έμ).
flask run
cli(κ°λ° μ μ©)μμ μ°λ¦¬λ μ€μ λ‘ μ± ν©ν 리μ λν λͺ
μμ μ§μ μ μΆκ°νμ΅λλ€.
λ¬Όλ‘ , Aκ° μμ± wsgi.py
ν¬ν¨ from myapp. import make_app; app = make_app()
μ½μ΅λλ€. κ·Έλ¬λ μ΄ νμΌμ λ³λλ‘ μ μ§ κ΄λ¦¬ν΄μΌ νκ±°λ(μ΄μ 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
λ¬Όλ‘ , Aκ° μμ±
wsgi.py
ν¬ν¨from myapp. import make_app; app = make_app()
μ½μ΅λλ€. νμ§λ§ μ΄ νμΌμ λ³λλ‘ μ μ§ κ΄λ¦¬ν΄μΌ νκ±°λ(μ΄μ pip install myapp
κ° μ€ννλ λ° νμν λͺ¨λ κ²μ μ€μΉνμ§ μκΈ° λλ¬Έμ λΆνΈν¨) ν¨ν€μ§μ λ£μ΄μΌ ν©λλ€(μ¦, μ΄μ λ΄λΆμμ νμΌμ κ°μ Έμ¬ μ μμμ μλ―Έν©λλ€. μλͺ»λ μ± μ체)
νλ‘μ νΈμ wsgi.py
κ° μλ λ λ€λ₯Έ μ΄μ λ μλͺ»λ κ²μ
λλ€. μΌλΆ λꡬλ νλ‘μ νΈμ λͺ¨λ λͺ¨λμ κ°μ Έμ΅λλ€. μλ₯Ό λ€μ΄ pytestλ doctestλ₯Ό μ°Ύμ λ μνν©λλ€.
μ¬κΈ° λ λ€λ₯Έ Flask μ μ§ κ΄λ¦¬μκ° μμ΅λλ€. @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μμ μ¬μ©ν μ μλ κΈ°λ₯μ λμ μ¬μ©ν΄μΌ ν λ "μ€, μ§κΈμ―€ μ΄ μ± κ°μ²΄λ₯Ό κ°μ ΈμμΌ ν©λλ€"λΌκ³ μκ°ν κ²μ΄λΌκ³ 보μ₯νκΈ° λλ¬Έμ λλ€.
μ΄ μμ μμ μ°λ¦¬κ° μꡬνλ κ²μ "κ°μ Έμ€κΈ° λ¬Έμμ΄μ΄ κ΄νΈλ‘ λλλ©΄ κ°μ Έμ¨ μ΄λ¦μ νΈμΆνμ¬ μ±μ κ°μ Έμ΅λλ€"μ λλ€.
λλ μ°λ¦¬κ° μ΄ λ¬Έμ λ₯Ό ν΄κ²°ν μ μλ λ°©λ²μ΄ λ§λ€κ³ μκ°νμ§λ§, κ·Έκ²μ μ°λ¦¬κ° νκ² μ²μ€μ΄ μλλΌλ κ²μ μλ―Έν©λλ€. ν¨ν€μ§ μΈλΆμ μλ μ€ν¬λ¦½νΈλ₯Ό 컨ν μ΄λμ μ§μ μ μΌλ‘ μ 곡νκ³ 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μμ server
μμ±μΌλ‘ λ΄λΆμ μΌλ‘ Flask
μΈμ€ν΄μ€κ° μλ Dash
κ°μ²΄λ₯Ό μ¬μ©ν©λλ€. μ΄λ€ μ¬λλ€μ λ€μμ μ¬μ©νκ³ μμμ΅λλ€.
gunicorn "module:app.server"
κ·Έλ¬λ μ΄κ²μ΄ μ§μλμ΄μΌνλμ§ νμ€νμ§ μμ΅λλ€. flask run
λ μ§μνμ§ μμ΅λλ€. Dash
κ°μ²΄μ Flask.__call__
μ λ¬λλ __call__
λ©μλκ° μμ΄μΌ νλ κ² κ°μ΅λλ€. λν Dashμ λ¬Έμ μλ server = app.server
νκ³ Gunicornμ κ°λ¦¬ν€λλ‘ λμ΄ μμΌλ―λ‘ λλΆλΆ μλͺ»λ μ λ³΄κ° μ λ¬λλ κ²½μ°μΈ κ² κ°μ΅λλ€.
@davidism μ λ μ€λ @tilgovi μ μ μμ
κ·Έλ° μ΄κΈ°νλ₯Ό μ¬μ©νκ³ μλ€κ³ μ¬μ©μμκ² κ²½κ³ ν΄μΌ νλ€κ³ μκ°ν©λλ€. μκ°? cc @tilgovi
λ€λ₯Έ ꡬνμ λ§λ€μ§ μλ ν μμμ λ§ν¬ν λΆκΈ°λ₯Ό ν μμΌμ ν μ€νΈκ° ν¬ν¨λ PRλ‘ μ ννλ €κ³ ν©λλ€.
@davidism κ°μΈμ. μΌμμΌμ λ€μ λ°©λ¬Ένκ² μ΅λλ€. νμν κ²½μ° κ²ν νκ² μ΅λλ€ :) κ°μ¬ν©λλ€!
μ‘°κΈ λ€μ, μ§κΈ μμ μ€μ λλ€.
@connorbrinton ast.parse
μ μ¬μ©νλ λ©μ§ μμ΄λμ΄ , μλν΄ λ³΄κ³ ν¨κ» κ°λ€λ©΄ 컀λ°μ 곡λ μμ±μλ‘ ν¬ν¨νκ² μ΅λλ€.
μ¬μ©μλ₯Ό v19 λμμΌλ‘ μλ΄νλ λ€μ μΈκΈ° μλ(κ·Έλ¦¬κ³ κ½€ μ€λλ) μ€ν μ€λ²νλ‘ λ΅λ³μ΄ μλ€λ μ μμ μ°¨μνκ³ μΆμμ΅λλ€. μ΄λ€ μ νμ νλμ§μ λ°λΌ μ λ°μ΄νΈκ° νμν μ μμ΅λλ€: https://stackoverflow.com/questions/ 8495367/using-additional-command-line-arguments-with-gunicorn
λ§μ€ν°μμ κ³ μ . ν¨μΉμ λν΄ @davidism μκ² κ°μ¬λ립λλ€!
μ²λ¦¬ λͺ¨λ κ²½μ°μμ΄ μνμ μμ΅λλ€ https://github.com/benoitc/gunicorn/commit/19cb68f4c3b55da22581c008659ee62d8c54ab2b#diff -5832adf374920d75d4bf48e546367f53R67
κ°μ₯ μ μ©ν λκΈ
λ§μ€ν°μμ κ³ μ . ν¨μΉμ λν΄ @davidism μκ² κ°μ¬λ립λλ€!
μ²λ¦¬ λͺ¨λ κ²½μ°μμ΄ μνμ μμ΅λλ€ https://github.com/benoitc/gunicorn/commit/19cb68f4c3b55da22581c008659ee62d8c54ab2b#diff -5832adf374920d75d4bf48e546367f53R67