Gunicorn: v20 문제: 'app'μ—μ„œ 'create_app()' μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 개체λ₯Ό 찾지 λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.

에 λ§Œλ“  2019λ…„ 11μ›” 09일  Β·  47μ½”λ©˜νŠΈ  Β·  좜처: benoitc/gunicorn

λ‚΄ λ²„μ „μ˜ 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'
( Feedback Requested FeaturApp Investigation

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

λ§ˆμŠ€ν„°μ—μ„œ κ³ μ •. νŒ¨μΉ˜μ— λŒ€ν•΄ @davidism μ—κ²Œ κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€!

처리 λͺ¨λ“  κ²½μš°μ—μ΄ μ‹œν—˜μ— μžˆμŠ΅λ‹ˆλ‹€ https://github.com/benoitc/gunicorn/commit/19cb68f4c3b55da22581c008659ee62d8c54ab2b#diff -5832adf374920d75d4bf48e546367f53R67

λͺ¨λ“  47 λŒ“κΈ€

λ‚˜λŠ” λ˜ν•œμ΄ 문제λ₯Ό κ²½ν—˜ν–ˆμŠ΅λ‹ˆλ‹€.
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. μš°λ¦¬λŠ” 이 λ™μž‘μ΄ μ§€μ›λ˜μ§€ μ•Šκ³  λ¬Έμ„œν™”λ˜μ§€ μ•Šμ€ κ²ƒμœΌλ‘œ κ°„μ£Όν•  수 μžˆμŠ΅λ‹ˆλ‹€(gunicornμ—μ„œ). 이λ₯Ό 기반으둜 ν•œ λ³€κ²½ μ‚¬ν•­μž…λ‹ˆλ‹€.
  2. 일뢀 μ‚¬μš©μžλŠ” 그것에 μ˜μ‘΄ν–ˆκ³  이제 μš°λ¦¬λŠ” κ·Έ 행동을 μ§€μ›ν•˜κ³ μž ν•©λ‹ˆλ‹€.

(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

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰