Python Flask как служба Windows - PullRequest
       77

Python Flask как служба Windows

15 голосов
/ 14 апреля 2019

Я пытаюсь запустить приложение Flask в качестве службы в Windows. Я уже пытался реализовать решение, предложенное здесь и здесь , но безуспешно.

У меня есть простая папка с двумя файлами:

Project
 |
 +-- myapp.py   
 +-- win32_service.py

Внутри myapp.py представляет собой простое Flask приложение:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

И каркас службы win32_service.py :

import win32serviceutil
import win32service
import win32event
import win32evtlogutil
import servicemanager
import socket
import time
import logging
import os
import sys

sys.path.append(os.path.dirname(__name__))

from myapp import app

logging.basicConfig(
    filename = r'c:\tmp\flask-service.log',
    level = logging.DEBUG, 
    format = '[flaskapp] %(levelname)-7.7s %(message)s'
)

class HelloFlaskSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "FlaskApp"
    _svc_display_name_ = "FlaskApp Service"

    def __init__(self, *args):
        win32serviceutil.ServiceFramework.__init__(self, *args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(5)
        self.stop_requested = False

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)
        logging.info('Stopped service ...')
        self.stop_requested = True

    def SvcDoRun(self):
        servicemanager.LogMsg(
            servicemanager.EVENTLOG_INFORMATION_TYPE,
            servicemanager.PYS_SERVICE_STARTED,
            (self._svc_name_,'')
        )

        self.main()

    def main(self):
        app.run(host="127.0.0.1", port=8000)

if __name__ == '__main__':
    if len(sys.argv) == 1:
        servicemanager.Initialize()
        servicemanager.PrepareToHostSingle(HelloFlaskSvc)
        servicemanager.StartServiceCtrlDispatcher()
    else:
        win32serviceutil.HandleCommandLine(HelloFlaskSvc)

Затем я скомпилировал это в файл exe через pyinstaller, используя эту команду:

pyinstaller --onefile --hidden-import win32timezone win32_service.py

Я успешно скомпилировал exe. Затем я продолжаю регистрировать службу (открыть cmd с правами администратора):

>>> win32_service.exe install
> Installing service FlaskApp
> Service installed

И я пытаюсь запустить его:

>>> win32_service.exe start
> Starting service FlaskApp

Но тогда ничего не происходит (без ошибок). Также, если я пытаюсь запустить его из диспетчера задач, он изменяет статус на Starting, а затем на Stopped.

Это модули, установленные в virtualenv :

altgraph==0.16.1
Click==7.0
Flask==1.0.2
future==0.17.1
itsdangerous==1.1.0
Jinja2==2.10.1
macholib==1.11
MarkupSafe==1.1.1
pefile==2018.8.8
PyInstaller==3.4
pyodbc==4.0.26
pywin32==224
pywin32-ctypes==0.2.0
Werkzeug==0.15.2

Технические характеристики системы :

Python - 3.6.5 
OS     - Windows 10

Что мне здесь не хватает? Любая помощь приветствуется.

EDIT

Windows EventViewer показывает ошибку:

The description for Event ID 3 from source FlaskApp cannot be found. Either the component that raises this event is not installed on your local computer or the installation is corrupted. You can install or repair the component on the local computer.

If the event originated on another computer, the display information had to be saved with the event.

The following information was included with the event: 

Traceback (most recent call last):
  File "lib\site-packages\win32\lib\win32serviceutil.py", line 839, in SvcRun
  File "win32_service.py", line 47, in SvcDoRun
  File "win32_service.py", line 50, in main
  File "lib\site-packages\flask\app.py", line 938, in run
  File "lib\site-packages\flask\cli.py", line 629, in show_server_banner
  File "lib\site-packages\click\utils.py", line 260, in echo
SystemError: <built-in method replace of str object at 0x000001E36AD465D0> returned a result with an error set

РЕДАКТИРОВАТЬ 2

Если я использую один файл spec, некоторые модули не обнаруживаются при скрытом импорте (это вывод из pyinstaller:

4972 INFO: Analyzing hidden import 'ClickFlask'
4973 ERROR: Hidden import 'ClickFlask' not found
4974 INFO: Analyzing hidden import 'future'
4981 INFO: Analyzing hidden import 'itsdangerous'
5029 INFO: Analyzing hidden import 'Jinja2'
5030 ERROR: Hidden import 'Jinja2' not found
5030 INFO: Analyzing hidden import 'MarkupSafe'
5032 ERROR: Hidden import 'MarkupSafe' not found
5033 INFO: Analyzing hidden import 'pyodbc'
5034 INFO: Analyzing hidden import 'pywin32'
5035 ERROR: Hidden import 'pywin32' not found
5035 INFO: Analyzing hidden import 'pywin32-ctypes'
5036 ERROR: Hidden import 'pywin32-ctypes' not found

Может ли это быть связано с этим? Почему некоторые модули найдены, а другие нет? Я использую virtualenv.

Ответы [ 2 ]

4 голосов
/ 17 апреля 2019

Я заглянул в pyinstaller github repo и решил эту проблему.

Кажется, что pyinstaller имеет некоторые конфликты с Windows 10, но эта проблема была ключом к моей проблеме. Хотя модуль, выдающий ошибку, не был тем же самым.

Мне удалось решить эту проблему, добавив исключение SystemError в lib\site-packages\click\utils.py, line 260 в функции echo.

Итак, я изменяю это:

if message:
   file.write(message)

К этому:

if message:
    try:
        file.write(message)
    except SystemError:
        pass

Восстановить exe с помощью:

pyinstaller --onefile --hidden-import win32timezone win32_service.py

Установил службу, а затем она запустилась корректно.

4 голосов
/ 17 апреля 2019

Согласно сообщению Reddit , Добавление всех библиотек к hiddenimports должно решить вашу проблему, я попробовал сам, и это сработало!

Итак, создайте файл в вашемКаталог проекта с именем win32_service.spec со следующим содержимым

# -*- mode: python -*-

block_cipher = None


a = Analysis(['win32_service.py'],
             pathex=['C:\\Users\\Win7\\Desktop\\FaaS'],
             binaries=[],
             datas=[],
             hiddenimports=['win32timezone',
                            'altgraph',
                            'Click'
                            'Flask',
                            'future',
                            'itsdangerous',
                            'Jinja2',
                            'macholib',
                            'MarkupSafe',
                            'pefile',
                            'PyInstaller',
                            'pyodbc',
                            'pywin32',
                            'pywin32-ctypes',
                            'Werkzeug',],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='win32_service',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          runtime_tmpdir=None,
          console=True )

Не забудьте изменить pathex переменную

Тогда вместо pyinstaller --onefile --hidden-import win32timezone win32_service.py используйте следующую команду: pyinstaller --onefile win32_service.spec

...