Windows run python script as service

I am sketching the architecture for a set of programs that share various interrelated objects stored in a database. I want one of the programs to act as a service which provides a higher level inte...

This answer is plagiarizer from several sources on StackOverflow — most of them above, but I’ve forgotten the others — sorry. It’s simple and scripts run «as is». For releases you test you script, then copy it to the server and Stop/Start the associated service. And it should work for all scripting languages (Python, Perl, node.js), plus batch scripts such as GitBash, PowerShell, even old DOS bat scripts.
pyGlue is the glue that sits between Windows Services and your script.

'''
A script to create a Windows Service, which, when started, will run an executable with the specified parameters.
Optionally, you can also specify a startup directory

To use this script you MUST define (in class Service)
1. A name for your service (short - preferably no spaces)
2. A display name for your service (the name visibile in Windows Services)
3. A description for your service (long details visible when you inspect the service in Windows Services)
4. The full path of the executable (usually C:/Python38/python.exe or C:WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe
5. The script which Python or PowerShell will run(or specify None if your executable is standalone - in which case you don't need pyGlue)
6. The startup directory (or specify None)
7. Any parameters for your script (or for your executable if you have no script)

NOTE: This does not make a portable script.
The associated '_svc_name.exe' in the dist folder will only work if the executable,
(and any optional startup directory) actually exist in those locations on the target system

Usage: 'pyGlue.exe [options] install|update|remove|start [...]|stop|restart [...]|debug [...]'
Options for 'install' and 'update' commands only:
        --username domain\username : The Username the service is to run under
        --password password : The password for the username
        --startup [manual|auto|disabled|delayed] : How the service starts, default = manual
        --interactive : Allow the service to interact with the desktop.
        --perfmonini file: .ini file to use for registering performance monitor data
        --perfmondll file: .dll file to use when querying the service for performance data, default = perfmondata.dll
Options for 'start' and 'stop' commands only:
        --wait seconds: Wait for the service to actually start or stop.
                If you specify --wait with the 'stop' option, the service and all dependent services will be stopped,
                each waiting the specified period.
'''

# Import all the modules that make life easy
import servicemanager
import socket
import sys
import win32event
import win32service
import win32serviceutil
import win32evtlogutil
import os
from logging import Formatter, Handler
import logging
import subprocess


# Define the win32api class
class Service (win32serviceutil.ServiceFramework):
        # The following variable are edited by the build.sh script
        _svc_name_ = "TestService"
        _svc_display_name_ = "Test Service"
        _svc_description_ = "Test Running Python Scripts as a Service"
        service_exe = 'c:/Python27/python.exe'
        service_script = None
        service_params = []
        service_startDir = None

        # Initialize the service
        def __init__(self, args):
                win32serviceutil.ServiceFramework.__init__(self, args)
                self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
                self.configure_logging()
                socket.setdefaulttimeout(60)

        # Configure logging to the WINDOWS Event logs
        def configure_logging(self):
                self.formatter = Formatter('%(message)s')
                self.handler = logHandler()
                self.handler.setFormatter(self.formatter)
                self.logger = logging.getLogger()
                self.logger.addHandler(self.handler)
                self.logger.setLevel(logging.INFO)

        # Stop the service
        def SvcStop(self):
                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
                win32event.SetEvent(self.hWaitStop)

        # Run the service
        def SvcDoRun(self):
                self.main()

        # This is the service
        def main(self):

                # Log that we are starting
                servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
                                                          (self._svc_name_, ''))

                # Fire off the real process that does the real work
                logging.info('%s - about to call Popen() to run %s %s %s', self._svc_name_, self.service_exe, self.service_script, self.service_params)
                self.process = subprocess.Popen([self.service_exe, self.service_script] + self.service_params, shell=False, cwd=self.service_startDir)
                logging.info('%s - started process %d', self._svc_name_, self.process.pid)

                # Wait until WINDOWS kills us - retrigger the wait for stop every 60 seconds
                rc = None
                while rc != win32event.WAIT_OBJECT_0:
                        rc = win32event.WaitForSingleObject(self.hWaitStop, (1 * 60 * 1000))

                # Shut down the real process and exit
                logging.info('%s - is terminating process %d', self._svc_name_, self.process.pid)
                self.process.terminate()
                logging.info('%s - is exiting', self._svc_name_)


class logHandler(Handler):
        '''
Emit a log record to the WINDOWS Event log
        '''

        def emit(self, record):
                servicemanager.LogInfoMsg(record.getMessage())


# The main code
if __name__ == '__main__':
        '''
Create a Windows Service, which, when started, will run an executable with the specified parameters.
        '''

        # Check that configuration contains valid values just in case this service has accidentally
        # been moved to a server where things are in different places
        if not os.path.isfile(Service.service_exe):
                print('Executable file({!s}) does not exist'.format(Service.service_exe), file=sys.stderr)
                sys.exit(0)
        if not os.access(Service.service_exe, os.X_OK):
                print('Executable file({!s}) is not executable'.format(Service.service_exe), file=sys.stderr)
                sys.exit(0)
        # Check that any optional startup directory exists
        if (Service.service_startDir is not None) and (not os.path.isdir(Service.service_startDir)):
                print('Start up directory({!s}) does not exist'.format(Service.service_startDir), file=sys.stderr)
                sys.exit(0)

        if len(sys.argv) == 1:
                servicemanager.Initialize()
                servicemanager.PrepareToHostSingle(Service)
                servicemanager.StartServiceCtrlDispatcher()
        else:
                # install/update/remove/start/stop/restart or debug the service
                # One of those command line options must be specified
                win32serviceutil.HandleCommandLine(Service)

Now there’s a bit of editing and you don’t want all your services called ‘pyGlue’. So there’s a script (build.sh) to plug in the bits and create a customized ‘pyGlue’ and create an ‘.exe’. It is this ‘.exe’ which gets installed as a Windows Service. Once installed you can set it to run automatically.

#!/bin/sh
# This script build a Windows Service that will install/start/stop/remove a service that runs a script
# That is, executes Python to run a Python script, or PowerShell to run a PowerShell script, etc

if [ $# -lt 6 ]; then
        echo "Usage: build.sh Name Display Description Executable Script StartupDir [Params]..."
        exit 0
fi

name=$1
display=$2
desc=$3
exe=$4
script=$5
startDir=$6
shift; shift; shift; shift; shift; shift
params=
while [ $# -gt 0 ]; do
        if [ "${params}" != "" ]; then
                params="${params}, "
        fi
        params="${params}'$1'"
        shift
done

cat pyGlue.py | sed -e "s/pyGlue/${name}/g" | 
        sed -e "/_svc_name_ =/s?=.*?= '${name}'?" | 
        sed -e "/_svc_display_name_ =/s?=.*?= '${display}'?" | 
        sed -e "/_svc_description_ =/s?=.*?= '${desc}'?" | 
        sed -e "/service_exe =/s?=.*?= '$exe'?" | 
        sed -e "/service_script =/s?=.*?= '$script'?" | 
        sed -e "/service_params =/s?=.*?= [${params}]?" | 
        sed -e "/service_startDir =/s?=.*?= '${startDir}'?" > ${name}.py

cxfreeze ${name}.py --include-modules=win32timezone

Installation — copy the ‘.exe’ the server and the script to the specified folder. Run the ‘.exe’, as Administrator, with the ‘install’ option. Open Windows Services, as Adminstrator, and start you service. For upgrade, just copy the new version of the script and Stop/Start the service.

Now every server is different — different installations of Python, different folder structures. I maintain a folder for every server, with a copy of pyGlue.py and build.sh. And I create a ‘serverBuild.sh’ script for rebuilding all the service on that server.

# A script to build all the script based Services on this PC
sh build.sh AutoCode 'AutoCode Medical Documents' 'Autocode Medical Documents to SNOMED_CT and AIHW codes' C:/Python38/python.exe autocode.py C:/Users/russell/Documents/autocoding -S -T

The web is a living, breathing organism – it constantly adapts and changes. In this dynamic environment, gathering time-sensitive data such as E-commerce listings only once is useless as it quickly becomes obsolete. To be competitive, you must keep your data fresh and run your web scraping scripts repeatedly and regularly.

The easiest way is to run a script in the background. In other words, run it as a service. Fortunately, no matter the operating system in use – Linux or Windows – you have great tools at your disposal. This guide will detail the process in a few simple steps.

Preparing a Python script for Linux

In this article, information from a list of book URLs will be scraped. When the process reaches the end of the list, it loops over and refreshes the data again and again.

First, make a request and retrieve the HTML content of a page. Use the Requests module to do so:

urls = [
'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
]

index = 0
while True:
    url = urls[index % len(urls)]
    index += 1

    print('Scraping url', url)
    response = requests.get(url)

Once the content is retrieved, parse it using the Beautiful Soup library:

soup = BeautifulSoup(response.content, 'html.parser')
book_name = soup.select_one('.product_main').h1.text
rows = soup.select('.table.table-striped tr')
product_info = {row.th.text: row.td.text for row in rows}

Make sure your data directory-to-be already exists, and then save book information there in JSON format.

Protip: make sure to use the pathlib module to automatically convert Python path separators into a format compatible with both Windows and Linux systems.

data_folder = Path('./data')
data_folder.mkdir(parents=True, exist_ok=True)

json_file_name = re.sub('[: ]', '-', book_name)
json_file_path = data_folder / f'{json_file_name}.json'
with open(json_file_path, 'w') as book_file:
    json.dump(product_info, book_file)

Since this script is long-running and never exits, you must also handle any requests from the operating system attempting to shut down the script. This way, you can finish the current iteration before exiting. To do so, you can define a class that handles the operating system signals:

class SignalHandler:
    shutdown_requested = False

    def __init__(self):
        signal.signal(signal.SIGINT, self.request_shutdown)
        signal.signal(signal.SIGTERM, self.request_shutdown)

    def request_shutdown(self, *args):
        print('Request to shutdown received, stopping')
        self.shutdown_requested = True

    def can_run(self):
        return not self.shutdown_requested

Instead of having a loop condition that never changes (while True), you can ask the newly built SignalHandler whether any shutdown signals have been received:

signal_handler = SignalHandler()

# ...

while signal_handler.can_run():
    # run the code only if you don't need to exit

Here’s the code so far:

import json
import re
import signal
from pathlib import Path

import requests
from bs4 import BeautifulSoup

       class SignalHandler:
    shutdown_requested = False

    def __init__(self):
        signal.signal(signal.SIGINT, self.request_shutdown)
        signal.signal(signal.SIGTERM, self.request_shutdown)

    def request_shutdown(self, *args):
        print('Request to shutdown received, stopping')
        self.shutdown_requested = True

    def can_run(self):
        return not self.shutdown_requested


signal_handler = SignalHandler()
urls = [
    'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
    'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
    'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
]

index = 0
while signal_handler.can_run():
    url = urls[index % len(urls)]
    index += 1

    print('Scraping url', url)
    response = requests.get(url)

    soup = BeautifulSoup(response.content, 'html.parser')
    book_name = soup.select_one('.product_main').h1.text
    rows = soup.select('.table.table-striped tr')
    product_info = {row.th.text: row.td.text for row in rows}

    data_folder = Path('./data')
    data_folder.mkdir(parents=True, exist_ok=True)

    json_file_name = re.sub('[': ]', '-', book_name)
    json_file_path = data_folder / f'{json_file_name}.json'
    with open(json_file_path, 'w') as book_file:
        json.dump(product_info, book_file)

The script will refresh JSON files with newly collected book information.

Running a Linux daemon

If you’re wondering how to run Python script in Linux, there are multiple ways to do it on startup. Many distributions have built-in GUI tools for such purposes.

Let’s use one of the most popular distributions, Linux Mint, as an example. It uses a desktop environment called Cinnamon that provides a startup application utility.

It allows you to add your script and specify a startup delay.

However, this approach doesn’t provide more control over the script. For example, what happens when you need to restart it?

This is where systemd comes in. Systemd is a service manager that allows you to manage user processes using easy-to-read configuration files.

To use systemd, let’s first create a file in the /etc/systemd/system directory:

cd /etc/systemd/system
touch book-scraper.service

Add the following content to the book-scraper.service file using your favorite editor:

[Unit]
Description=A script for scraping the book information
After=syslog.target network.target

[Service]
WorkingDirectory=/home/oxylabs/Scraper
ExecStart=/home/oxylabs/Scraper/venv/bin/python3 scrape.py

Restart=always
RestartSec=120

[Install]
WantedBy=multi-user.target

Here’s the basic rundown of the parameters used in the configuration file:

  • After – ensures you only start your Python script once the network is up. 

  • RestartSec – sleep time before restarting the service.

  • Restart – describes what to do if a service exits, is killed, or a timeout is reached. 

  • WorkingDirectory – current working directory of the script.

  • ExecStart – the command to execute.

Now, it’s time to tell systemd about the newly created daemon. Run the daemon-reload command:

Then, start your service:

systemctl start book-scraper

And finally, check whether your service is running:

$  system systemctl status book-scraper
book-scraper.service - A script for scraping the book information
     Loaded: loaded (/etc/systemd/system/book-scraper.service; disabled; vendor preset: enabled)
     Active: active (running) since Thu 2022-09-08 15:01:27 EEST; 16min ago
   Main PID: 60803 (python3)
      Tasks: 1 (limit: 18637)
     Memory: 21.3M
     CGroup: /system.slice/book-scraper.service
             60803 /home/oxylabs/Scraper/venv/bin/python3 scrape.py

Sep 08 15:17:55 laptop python3[60803]: Scraping url https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html
Sep 08 15:17:55 laptop python3[60803]: Scraping url https://books.toscrape.com/catalogue/sharp-objects_997/index.html
Sep 08 15:17:55 laptop python3[60803]: Scraping url https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html

Protip: use journalctl -S today -u book-scraper.service to monitor your logs in real-time.

Congrats! Now you can control your service via systemd.

Running a Python script as a Windows service

Running a Python script as a Windows service is not as straightforward as one might expect. Let’s start with the script changes.

To begin, change how the script is executed based on the number of arguments it receives from the command line.  

If the script receives a single argument, assume that Windows Service Manager is attempting to start it. It means that you have to run an initialization code. If zero arguments are passed, print some helpful information by using win32serviceutil.HandleCommandLine:

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

Next, extend the special utility class and set some properties. The service name, display name, and description will all be visible in the Windows services utility (services.msc) once your service is up and running.

class BookScraperService(win32serviceutil.ServiceFramework):
    _svc_name_ = 'BookScraperService'
    _svc_display_name_ = 'BookScraperService'
    _svc_description_ = 'Constantly updates the info about books'

Finally, implement the SvcDoRun and SvcStop methods to start and stop the service. Here’s the script so far:

import sys
import servicemanager
import win32event
import win32service
import win32serviceutil
import json
import re
from pathlib import Path

import requests
from bs4 import BeautifulSoup


class BookScraperService(win32serviceutil.ServiceFramework):
    _svc_name_ = 'BookScraperService'
    _svc_display_name_ = 'BookScraperService'
    _svc_description_ = 'Constantly updates the info about books'

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.event = win32event.CreateEvent(None, 0, 0, None)

    def GetAcceptedControls(self):
        result = win32serviceutil.ServiceFramework.GetAcceptedControls(self)
        result |= win32service.SERVICE_ACCEPT_PRESHUTDOWN
        return result

    def SvcDoRun(self):
        urls = [
'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
        ]

        index = 0

        while True:
            result = win32event.WaitForSingleObject(self.event, 5000)
            if result == win32event.WAIT_OBJECT_0:
                break

            url = urls[index % len(urls)]
            index += 1

            print('Scraping url', url)
            response = requests.get(url)

            soup = BeautifulSoup(response.content, 'html.parser')
            book_name = soup.select_one('.product_main').h1.text
            rows = soup.select('.table.table-striped tr')
            product_info = {row.th.text: row.td.text for row in rows}

            data_folder = Path('C:\Users\User\Scraper\dist\scrape\data')
            data_folder.mkdir(parents=True, exist_ok=True)

            json_file_name = re.sub('[': ]', '-', book_name)
            json_file_path = data_folder / f'{json_file_name}.json'
            with open(json_file_path, 'w') as book_file:
                json.dump(product_info, book_file)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.event)


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

Now that you have the script, open a Windows terminal of your preference.

Protip: if you’re using Powershell, make sure to include a .exe extension when running binaries to avoid unexpected errors.

Once the terminal is open, change the directory to the location of your script with a virtual environment, for example:

Next, install the experimental Python Windows extensions module, pypiwin32. You’ll also need to run the post-install script:

.venvScriptspip install pypiwin32
.venvScriptspywin32_postinstall.py -install

Unfortunately, if you attempt to install your Python script as a Windows service with the current setup, you’ll get the following error:

**** WARNING ****
The executable at "C:UsersUserScrapervenvlibsite-packageswin32PythonService.exe" is being used as a service.

This executable doesn't have pythonXX.dll and/or pywintypesXX.dll in the same
directory, and they can't be found in the System directory. This is likely to
fail when used in the context of a service.

The exact environment needed will depend on which user runs the service and
where Python is installed. If the service fails to run, this will be why.

NOTE: You should consider copying this executable to the directory where these
DLLs live - "C:UsersUserScrapervenvlibsite-packageswin32" might be a good place.

However, if you follow the instructions of the error output, you’ll be met with a new issue when trying to launch your script:

Error starting service: The service did not respond to the start or control request in a timely fashion.

To solve this issue, you can add the Python libraries and interpreter to the Windows path. Alternatively, bundle your script and all its dependencies into an executable by using pyinstaller:

venvScriptspyinstaller --hiddenimport win32timezone -F scrape.py

The —hiddenimport win32timezone option is critical as the win32timezone module is not explicitly imported but is still needed for the script to run.

Finally, let’s install the script as a service and run it by invoking the executable you’ve built previously:

PS C:UsersUserScraper> .distscrape.exe install
Installing service BookScraper
Changing service configuration
Service updated

PS C:UsersUserScraper> .distscrape.exe start
Starting service BookScraper
PS C:UsersUserScraper>

And that’s it. Now, you can open the Windows services utility and see your new service running.

Protip: you can read more about specific Windows API functions here.

The newly created service is running

Making your life easier by using NSSM on Windows

As evident, you can use win32serviceutil to develop a Windows service. But the process is definitely not that simple – you could even say it sucks! Well, this is where the NSSM (Non-Sucking Service Manager) comes into play.

Let’s simplify the script by only keeping the code that performs web scraping:

import json
import re
from pathlib import Path

import requests
from bs4 import BeautifulSoup

urls = ['https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
        'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
        'https://books.toscrape.com/catalogue/sharp-objects_997/index.html', ]

index = 0

while True:
    url = urls[index % len(urls)]
    index += 1

    print('Scraping url', url)
    response = requests.get(url)

    soup = BeautifulSoup(response.content, 'html.parser')
    book_name = soup.select_one('.product_main').h1.text
    rows = soup.select('.table.table-striped tr')
    product_info = {row.th.text: row.td.text for row in rows}

    data_folder = Path('C:\Users\User\Scraper\data')
    data_folder.mkdir(parents=True, exist_ok=True)

    json_file_name = re.sub('[': ]', '-', book_name)
    json_file_path = data_folder / f'{json_file_name}.json'
    with open(json_file_path, 'w') as book_file:
        json.dump(product_info, book_file)

Next, build a binary using pyinstaller:

venvScriptspyinstaller -F simple_scrape.py

Now that you have a binary, it’s time to install NSSM by visiting the official website. Extract it to a folder of your choice and add the folder to your PATH environment variable for convenience.

Then, run the terminal as an admin.

Once the terminal is open, change the directory to your script location:

Finally, install the script using NSSM and start the service:

nssm.exe install SimpleScrape C:UsersUserScraperdistsimple_scrape.exe
nssm.exe start SimpleScrape

Protip: if you have issues, redirect the standard error output of your service to a file to see what went wrong:

nssm set SimpleScrape AppStderr C:UsersUserScraperservice-error.log

NSSM ensures that a service is running in the background, and if it doesn’t, you at least get to know why.

Conclusion

Regardless of the operating system, you have various options for setting up Python scripts for recurring web scraping tasks. Whether you need the configurability of systemd, the flexibility of Windows services, or the simplicity of NSSM, be sure to follow this tried & true guide as you navigate their features.

If you are interested in more Python automation solutions for web scraping applications or would like to refresh the basics, take a look at our blog for various tutorials on all things web scraping.

People also ask

What is systemd?

Systemd is a powerful tool for managing processes. It also provides logging and dependency management, for example, starting processes only when a network is available or once a previous service has been started.

What is the difference between systemd and systemctl?

Systemctl is a utility and is part of systemd. Among other things, it allows you to manage services and check their status.

Can I use cron instead of systemd?

You definitely can if it suits your needs. However, if you need more control over the service, systemd is the way to go. It allows you to start services based on certain conditions and is perfect for dealing with long-running scripts. It even allows you to run scripts in a similar way to crontab by using timers.

What’s the difference between a daemon and a service?

The word daemon comes from MIT’s Project MAC, which first coined the term in 1963. It is, by definition, an agent that works tirelessly in the background. Later Windows and Linux adopted the term as an alternative name. Therefore, this article uses the words daemon and service interchangeably.

About the author

Augustas Pelakauskas

Senior Copywriter

Augustas Pelakauskas is a Senior Copywriter at Oxylabs. Coming from an artistic background, he is deeply invested in various creative ventures — the most recent one being writing. After testing his abilities in the field of freelance journalism, he transitioned to tech content creation. When at ease, he enjoys sunny outdoors and active recreation. As it turns out, his bicycle is his fourth best friend.

All information on Oxylabs Blog is provided on an «as is» basis and for informational purposes only. We make no representation and disclaim all liability with respect to your use of any information contained on Oxylabs Blog or any third-party websites that may be linked therein. Before engaging in scraping activities of any kind you should consult your legal advisors and carefully read the particular website’s terms of service or receive a scraping license.

Playwright vs Puppeteer: The Differences

Augustas Pelakauskas

2023-01-24

Playwright vs Selenium: Which One to Choose

Enrika Pavlovskytė

2023-01-10

Introducing Real Estate Scraper API: Gather Property Data at Scale

Danielius Radavicius

2022-11-24

IN THIS ARTICLE:


  • Preparing a Python script for Linux


  • Running a Linux daemon


  • Running a Python script as a Windows service


  • Making your life easier by using NSSM on Windows


  • Conclusion

Python Script As A Service



  • Setting Up
  • Create A Systemd Service
  • Create A Windows Service
  • Easier Windows Service Using NSSM

A service (also known as a «daemon») is a process that performs tasks in the background and responds to system events.

Services can be written using any language. We use Python in these examples as it is one of the most versatile languages out there.

For more information, be sure to read our blogpost on the subject.

Setting Up

To run any of the examples, you will need Python 3. We also recommend using a virtual environment.

Create A Systemd Service

First, create a script that scrapes a website. Make sure the script also handles OS signals so that it exits gracefully.

linux_scrape.py:

import json
import re
import signal
from pathlib import Path

import requests
from bs4 import BeautifulSoup

class SignalHandler:
    shutdown_requested = False

    def __init__(self):
        signal.signal(signal.SIGINT, self.request_shutdown)
        signal.signal(signal.SIGTERM, self.request_shutdown)

    def request_shutdown(self, *args):
        print('Request to shutdown received, stopping')
        self.shutdown_requested = True

    def can_run(self):
        return not self.shutdown_requested


signal_handler = SignalHandler()
urls = [
    'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
    'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
    'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
]

index = 0
while signal_handler.can_run():
    url = urls[index % len(urls)]
    index += 1

    print('Scraping url', url)
    response = requests.get(url)

    soup = BeautifulSoup(response.content, 'html.parser')
    book_name = soup.select_one('.product_main').h1.text
    rows = soup.select('.table.table-striped tr')
    product_info = {row.th.text: row.td.text for row in rows}

    data_folder = Path('./data')
    data_folder.mkdir(parents=True, exist_ok=True)

    json_file_name = re.sub('[': ]', '-', book_name)
    json_file_path = data_folder / f'{json_file_name}.json'
    with open(json_file_path, 'w') as book_file:
        json.dump(product_info, book_file)

Then, create a systemd configuration file.

/etc/systemd/system/book-scraper.service:

[Unit]
Description=A script for scraping the book information
After=syslog.target network.target

[Service]
WorkingDirectory=/home/oxylabs/python-script-service/src/systemd
ExecStart=/home/oxylabs/python-script-service/venv/bin/python3 main.py

Restart=always
RestartSec=120

[Install]
WantedBy=multi-user.target

Make sure to adjust the paths based on your actual script location.

A fully working example can be found here.

Create A Windows Service

To create a Windows service, you will need to implement methods such as SvcDoRun and SvcStop and handle events sent by the operating system.

windows_scrape.py:

import sys
import servicemanager
import win32event
import win32service
import win32serviceutil
import json
import re
from pathlib import Path

import requests
from bs4 import BeautifulSoup


class BookScraperService(win32serviceutil.ServiceFramework):
    _svc_name_ = 'BookScraperService'
    _svc_display_name_ = 'BookScraperService'
    _svc_description_ = 'Constantly updates the info about books'

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.event = win32event.CreateEvent(None, 0, 0, None)

    def GetAcceptedControls(self):
        result = win32serviceutil.ServiceFramework.GetAcceptedControls(self)
        result |= win32service.SERVICE_ACCEPT_PRESHUTDOWN
        return result

    def SvcDoRun(self):
        urls = [
'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
        ]

        index = 0

        while True:
            result = win32event.WaitForSingleObject(self.event, 5000)
            if result == win32event.WAIT_OBJECT_0:
                break

            url = urls[index % len(urls)]
            index += 1

            print('Scraping url', url)
            response = requests.get(url)

            soup = BeautifulSoup(response.content, 'html.parser')
            book_name = soup.select_one('.product_main').h1.text
            rows = soup.select('.table.table-striped tr')
            product_info = {row.th.text: row.td.text for row in rows}

            data_folder = Path('C:\Users\User\Scraper\dist\scrape\data')
            data_folder.mkdir(parents=True, exist_ok=True)

            json_file_name = re.sub('[': ]', '-', book_name)
            json_file_path = data_folder / f'{json_file_name}.json'
            with open(json_file_path, 'w') as book_file:
                json.dump(product_info, book_file)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.event)


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

Next, install dependencies and run a post-install script.

PS C:> cd C:UsersUserScraper
PS C:UsersUserScraper> .venvScriptspip install pypiwin32
PS C:UsersUserScraper> .venvScriptspywin32_postinstall.py -install

Bundle your script into an executable.

PS C:UsersUserScraper> venvScriptspyinstaller --hiddenimport win32timezone -F scrape.py

And finally, install your newly-created service.

PS C:UsersUserScraper> .distscrape.exe install
Installing service BookScraper
Changing service configuration
Service updated

PS C:UsersUserScraper> .distscrape.exe start
Starting service BookScraper
PS C:UsersUserScripts>

A fully working example can be found here.

Easier Windows Service Using NSSM

Instead of dealing with the Windows service layer directly, you can use the NSSM (Non-Sucking Service Manager).

Install NSSM by visiting the official website. Extract it to a folder of your choice and add the folder to your PATH environment variable for convenience.

Once you have NSSM installed, simplify your script by getting rid of all Windows-specific methods and definitions.

simple_scrape.py:

import json
import re
from pathlib import Path

import requests
from bs4 import BeautifulSoup

urls = ['https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
        'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
        'https://books.toscrape.com/catalogue/sharp-objects_997/index.html', ]

index = 0

while True:
    url = urls[index % len(urls)]
    index += 1

    print('Scraping url', url)
    response = requests.get(url)

    soup = BeautifulSoup(response.content, 'html.parser')
    book_name = soup.select_one('.product_main').h1.text
    rows = soup.select('.table.table-striped tr')
    product_info = {row.th.text: row.td.text for row in rows}

    data_folder = Path('C:\Users\User\Scraper\data')
    data_folder.mkdir(parents=True, exist_ok=True)

    json_file_name = re.sub('[': ]', '-', book_name)
    json_file_path = data_folder / f'{json_file_name}.json'
    with open(json_file_path, 'w') as book_file:
        json.dump(product_info, book_file)

Bundle your script into an executable.

PS C:UsersUserScraper> venvScriptspyinstaller -F simple_scrape.py

And finally, install the script using NSSM.

PS C:> nssm.exe install SimpleScrape C:UsersUserScraperdistsimple_scrape.exe
PS C:UsersUserScraper> .venvScriptspip install pypiwin32

A fully working script can be found here.

Headless processes (with no UI) in Windows are called Services. They can be controlled (started, stopped, etc) using standard Windows controls such as the command console, Powershell or the Services tab in Task Manager. A good example might be an application that provides network services, such as a web application, or maybe a backup application that performs various background archival tasks. There are several ways to create and install a Python application as a Service in Windows.

# A Python script that can be run as a service

The modules used in this example are part of pywin32 (opens new window) (Python for Windows extensions). Depending on how you installed Python, you might need to install this separately.

This is just boilerplate. Your application code, probably invoking a separate script, would go in the main() function.

You will also need to install this as a service. The best solution for this at the moment appears to be to use Non-sucking Service Manager (opens new window). This allows you to install a service and provides a GUI for configuring the command line the service executes. For Python you can do this, which creates the service in one go:

Where my_script.py is the boilerplate script above, modified to invoke your application script or code in the main() function. Note that the service doesn’t run the Python script directly, it runs the Python interpreter and passes it the main script on the command line.

Alternatively you can use tools provided in the Windows Server Resource Kit for your operating system version so create the service.

# Running a Flask web application as a service

This is a variation on the generic example. You just need to import your app script and invoke it’s run() method in the service’s main() function. In this case we’re also using the multiprocessing module due to an issue accessing WSGIRequestHandler.

Adapted from http://stackoverflow.com/a/25130524/318488 (opens new window)

teaser

Hi guys, today’s post is just for the ones of you that work with the “OS of the misoriented slashes”: Microsoft Windows. :)

Have you ever had the need of writing a Python script that could run in background as a Windows Service? In this post, you will learn how to do it in less than 10 minutes, no jokes.

I will skip all the introduction about Windows Services, how convenient they could be, how much could be appreciated the fact that they can be run in background even when the user is logged off etc… I mean, if you can code in Python and you use Windows I bet you already know what a Windows Service is, don’t you?

So, first of all, let’s start by installing the Python for Windows extensions:

c:test> pip install pywin32

Once you have done it, let’s write this base class, your Windows service will be a subclass of this base class.

 1'''
 2SMWinservice
 3by Davide Mastromatteo
 4
 5Base class to create winservice in Python
 6-----------------------------------------
 7
 8Instructions:
 9
101. Just create a new class that inherits from this base class
112. Define into the new class the variables
12   _svc_name_ = "nameOfWinservice"
13   _svc_display_name_ = "name of the Winservice that will be displayed in scm"
14   _svc_description_ = "description of the Winservice that will be displayed in scm"
153. Override the three main methods:
16    def start(self) : if you need to do something at the service initialization.
17                      A good idea is to put here the inizialization of the running condition
18    def stop(self)  : if you need to do something just before the service is stopped.
19                      A good idea is to put here the invalidation of the running condition
20    def main(self)  : your actual run loop. Just create a loop based on your running condition
214. Define the entry point of your module calling the method "parse_command_line" of the new class
225. Enjoy
23'''
24
25import socket
26
27import win32serviceutil
28
29import servicemanager
30import win32event
31import win32service
32
33
34class SMWinservice(win32serviceutil.ServiceFramework):
35    '''Base class to create winservice in Python'''
36
37    _svc_name_ = 'pythonService'
38    _svc_display_name_ = 'Python Service'
39    _svc_description_ = 'Python Service Description'
40
41    @classmethod
42    def parse_command_line(cls):
43        '''
44        ClassMethod to parse the command line
45        '''
46        win32serviceutil.HandleCommandLine(cls)
47
48    def __init__(self, args):
49        '''
50        Constructor of the winservice
51        '''
52        win32serviceutil.ServiceFramework.__init__(self, args)
53        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
54        socket.setdefaulttimeout(60)
55
56    def SvcStop(self):
57        '''
58        Called when the service is asked to stop
59        '''
60        self.stop()
61        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
62        win32event.SetEvent(self.hWaitStop)
63
64    def SvcDoRun(self):
65        '''
66        Called when the service is asked to start
67        '''
68        self.start()
69        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
70                              servicemanager.PYS_SERVICE_STARTED,
71                              (self._svc_name_, ''))
72        self.main()
73
74    def start(self):
75        '''
76        Override to add logic before the start
77        eg. running condition
78        '''
79        pass
80
81    def stop(self):
82        '''
83        Override to add logic before the stop
84        eg. invalidating running condition
85        '''
86        pass
87
88    def main(self):
89        '''
90        Main class to be ovverridden to add logic
91        '''
92        pass
93
94# entry point of the module: copy and paste into the new module
95# ensuring you are calling the "parse_command_line" of the new created class
96if __name__ == '__main__':
97    SMWinservice.parse_command_line()

Let’s examine the class we have just introduced a little.

def SvcDoRun(self): it’s the method that will be called when the service is requested to start.

def SvcStop(self): it’s the method that will be called when the service is requested to stop.

def start(self): it’s a method that you will be asked to override if you need to do something when the service is starting (before started)

def stop(self): it’s the method that you will be asked to override if you need to do something when the service is stopping (before stopped)

def main(self): it’s the method that will contain the logic of your script, usually in a loop that keeps it alive until the service is stopped.

def parse_command_line(cls): it’s the method that handles the command line interface that you can use to install and update your windows service

Can you see how easy it is with pywin32 to interface with the system to create a Windows Service? The last mention is for the following variables:

1svc_name = "PythonCornerExample"
2svc_display_name = "Python Corner's Winservice Example"
3svc_description = "That's a great winservice! :)"

These are just three variables that contain the name of the service, the “friendly name” that will be used by Windows to display the name on the mmc console and a short description of your service.

As always, enough talk, let’s code something useful!


Let’s pretend that we want to create a Winservice that, when started, creates a random file on our C: drive every 5 seconds.

What? Do you think it is stupid?
Well, install it on your boss PC, set the destination folder as its user’s desktop and you will change your mind. :)

However, how can you achieve this result? Super easy.

  • Subclass the SMWinservice class we have just met.
  • On the new class, override the three variables svc_name, svc_display_name and svc_description.
  • Override the “start” method to set the running condition. Setting a boolean variable will be enough for that.
  • Override the “stop” method to invalidate the running condition when the service is requested to be stopped.
  • Override the “main” method to add the logic of creating a random file every 5 seconds
  • Add the call at the “parse_command_line” function to handle the command line interface.

The result should be something like this:

 1import time
 2import random
 3from pathlib import Path
 4from SMWinservice import SMWinservice
 5
 6class PythonCornerExample(SMWinservice):
 7    _svc_name_ = "PythonCornerExample"
 8    _svc_display_name_ = "Python Corner's Winservice Example"
 9    _svc_description_ = "That's a great winservice! :)"
10
11    def start(self):
12        self.isrunning = True
13
14    def stop(self):
15        self.isrunning = False
16
17    def main(self):
18        i = 0
19        while self.isrunning:
20            random.seed()
21            x = random.randint(1, 1000000)
22            Path(f'c:{x}.txt').touch()
23            time.sleep(5)
24
25if __name__ == '__main__':
26    PythonCornerExample.parse_command_line()

That’s it! Now it’s time to install our newly created winservice. Just open a command prompt, navigate to your script directory and install the service with the command:

C:test> python PythonCornerExample.py install
Installing service PythonCornerExample
Service installed

In the future, if you want to change the code of your service, just modify it and reinstall the service with

C:test> python PythonCornerExample.py update
Changing service configuration
Service updated

Now, open the “Services” msc snap in

C:test> mmc Services.msc

locate your new PythonCornerExample winservice, and right click and choose properties. Here you can start your service and configure it at your will.

Now try to start your service and go to see your C: folder contents.

Can you see all these files being created to your C: folder? Yeah, that’s working!

But now it’s time to stop it!  :) You can do it from the previous windows or just by using the command line

C:test> net stop PythonCornerExample

Il servizio Python Corner's Winservice Example sta per essere arrestato.. 
Servizio Python Corner's Winservice Example arrestato.

If something goes wrong…

There are a couple of known problems that can happen writing Windows Services in Python. If you have successfully installed the service but starting it you get an error, follow this iter to troubleshoot your service:

  • Check if Python is in your PATH variable. It MUST be there. To check this, just open a command prompt and try starting the python interpreter by typing “python”. If it starts, you are ok.
  • Be sure to have the file C:Program FilesPython36Libsite-packageswin32pywintypes36.dll (please note that “36” is the version of your Python installation). If you don’t have this file, take it from C:Program FilesPython36Libsite-packagespywin32_system32pywintypes36.dll and copy it into C:Program FilesPython36Libsite-packageswin32

If you still have problems, try executing your Python script in debug mode. To try this with our previous example, open a terminal, navigate to the directory where the script resides and type

c:test> python PythonCornerExample.py debug

Ok, that’s all for today, this was just a brief introduction to developing Windows Services with Python. Try it by yourself and … happy coding!

D.

Using python 2.7 and windows 7. I have a python file(.py). I want to launch this script as a windows services, i.e. this code will keep on running from start to end and never terminate.

I can convert .py file into an exe using py2exe. How to launch this and how the code flow works in services. Is it starts from line one to continue to last line, and then again starts from line one of code.

asked Mar 10, 2014 at 19:19

sherlock's user avatar

2

The simplest way is to use the: NSSM — the Non-Sucking Service Manager:

  1. Download on https://nssm.cc/download

  2. Install the Python program as a service: Win prompt as admin

    c:>nssm.exe install WinService
    
  3. In NSSM’s console:

    path: C:Python27Python27.exe
    Startup directory: C:Python27
    Arguments: c:WinService.py
    
  4. Check the created services on services.msc

Donald Duck's user avatar

Donald Duck

2,44510 gold badges29 silver badges45 bronze badges

answered Sep 27, 2017 at 14:09

adriano's user avatar

adrianoadriano

511 silver badge2 bronze badges

Sorry with my current level in superuser I can’t add a comment like my first answer.

Download the required lib with the right version of Python and Windows:

http://sourceforge.net/projects/pywin32/

To install the service, just type:

<Your python Path>python.exe "Path to your script".py install

I have tested the script in the link you gave me and it’s working correctly in my computer.

If you want to delete the service, just type in cmd (as admin):

sc delete "Service Name"

answered Mar 12, 2014 at 15:10

max890's user avatar

max890max890

2511 silver badge5 bronze badges

Solving problem is about exposing yourself to as many situations as possible like How do you run a Python script as a service in Windows? and practice these strategies over and over. With time, it becomes second nature and a natural way you approach any problems in general. Big or small, always start with a plan, use other strategies mentioned here till you are confident and ready to code the solution.
In this post, my aim is to share an overview the topic about How do you run a Python script as a service in Windows?, which can be followed any time. Take easy to follow this discuss.

How do you run a Python script as a service in Windows?

I am sketching the architecture for a set of programs that share various interrelated objects stored in a database. I want one of the programs to act as a service which provides a higher level interface for operations on these objects, and the other programs to access the objects through that service.

I am currently aiming for Python and the Django framework as the technologies to implement that service with. I’m pretty sure I figure how to daemonize the Python program in Linux. However, it is an optional spec item that the system should support Windows. I have little experience with Windows programming and no experience at all with Windows services.

Is it possible to run a Python programs as a Windows service (i. e. run it automatically without user login)? I won’t necessarily have to implement this part, but I need a rough idea how it would be done in order to decide whether to design along these lines.

Edit: Thanks for all the answers so far, they are quite comprehensive. I would like to know one more thing: How is Windows aware of my service? Can I manage it with the native Windows utilities? What is the equivalent of putting a start/stop script in /etc/init.d?

Answer #1:

Yes you can. I do it using the pythoncom libraries that come included with ActivePython or can be installed with pywin32 (Python for Windows extensions).

This is a basic skeleton for a simple service:

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "TestService"
    _svc_display_name_ = "Test Service"
    def __init__(self,args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(60)
    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_,''))
        self.main()
    def main(self):
        pass
if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

Your code would go in the main() method—usually with some kind of infinite loop that might be interrupted by checking a flag, which you set in the SvcStop method

Answer #2:

Although I upvoted the chosen answer a couple of weeks back, in the meantime I struggled a lot more with this topic. It feels like having a special Python installation and using special modules to run a script as a service is simply the wrong way. What about portability and such?

I stumbled across the wonderful Non-sucking Service Manager, which made it really simple and sane to deal with Windows Services. I figured since I could pass options to an installed service, I could just as well select my Python executable and pass my script as an option.

I have not yet tried this solution, but I will do so right now and update this post along the process. I am also interested in using virtualenvs on Windows, so I might come up with a tutorial sooner or later and link to it here.

Answer #3:

The simplest way is to use the: NSSM – the Non-Sucking Service Manager. Just download and unzip to a location of your choosing. It’s a self-contained utility, around 300KB (much less than installing the entire pywin32 suite just for this purpose) and no “installation” is needed. The zip contains a 64-bit and a 32-bit version of the utility. Either should work well on current systems (you can use the 32-bit version to manage services on 64-bit systems).

GUI approach

1 – install the python program as a service. Open a Win prompt as admin

c:>nssm.exe install WinService

2 – On NSSM´s GUI console:

path: C:Python27Python27.exe

Startup directory: C:Python27

Arguments: c:WinService.py

3 – check the created services on services.msc

Scripting approach (no GUI)

This is handy if your service should be part of an automated, non-interactive procedure, that may be beyond your control, such as a batch or installer script. It is assumed that the commands are executed with administrative privileges.

For convenience the commands are described here by simply referring to the utility as nssm.exe. It is advisable, however, to refer to it more explicitly in scripting with its full path c:pathtonssm.exe, since it’s a self-contained executable that may be located in a private path that the system is not aware of.

1. Install the service

You must specify a name for the service, the path to the proper Python executable, and the path to the script:

nssm.exe install ProjectService "c:pathtopython.exe" "c:pathtoprojectappmain.py"

More explicitly:

nssm.exe install ProjectService
nssm.exe set ProjectService Application "c:pathtopython.exe"
nssm.exe set ProjectService AppParameters "c:pathtoprojectappmain.py"

Alternatively you may want your Python app to be started as a Python module. One easy approach is to tell nssm that it needs to change to the proper starting directory, as you would do yourself when launching from a command shell:

nssm.exe install ProjectService "c:pathtopython.exe" "-m app.main"
nssm.exe set ProjectService AppDirectory "c:pathtoproject"

This approach works well with virtual environments and self-contained (embedded) Python installs. Just make sure to have properly resolved any path issues in those environments with the usual methods. nssm has a way to set environment variables (e.g. PYTHONPATH) if needed, and can also launch batch scripts.

2. To start the service

nssm.exe start ProjectService

3. To stop the service

nssm.exe stop ProjectService

4. To remove the service, specify the confirm parameter to skip the interactive confirmation.

nssm.exe remove ProjectService confirm

Answer #5:

There are a couple alternatives for installing as a service virtually any Windows executable.

Method 1: Use instsrv and srvany from rktools.exe

For Windows Home Server or Windows Server 2003 (works with WinXP too), the Windows Server 2003 Resource Kit Tools comes with utilities that can be used in tandem for this, called instsrv.exe and srvany.exe. See this Microsoft KB article KB137890 for details on how to use these utils.

For Windows Home Server, there is a great user friendly wrapper for these utilities named aptly “Any Service Installer“.

Method 2: Use ServiceInstaller for Windows NT

There is another alternative using ServiceInstaller for Windows NT (download-able here) with python instructions available. Contrary to the name, it works with both Windows 2000 and Windows XP as well. Here are some instructions for how to install a python script as a service.

Installing a Python script

Run ServiceInstaller to create a new
service. (In this example, it is
assumed that python is installed at
c:python25)

Service Name  : PythonTest
Display Name : PythonTest
Startup : Manual (or whatever you like)
Dependencies : (Leave blank or fill to fit your needs)
Executable : c:python25python.exe
Arguments : c:path_to_your_python_scripttest.py
Working Directory : c:path_to_your_python_script

After installing, open the Control
Panel’s Services applet, select and
start the PythonTest service.

After my initial answer, I noticed there were closely related Q&A already posted on SO. See also:

Can I run a Python script as a service (in Windows)? How?

How do I make Windows aware of a service I have written in Python?

Answer #6:

Step by step explanation how to make it work :

1- First create a python file according to the basic skeleton mentioned above. And save it to a path for example : “c:PythonFilesAppServerSvc.py”

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "TestService"
    _svc_display_name_ = "Test Service"
    def __init__(self,args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(60)
    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                          servicemanager.PYS_SERVICE_STARTED,
                          (self._svc_name_,''))
        self.main()
    def main(self):
        # Your business logic or call to any class should be here
        # this time it creates a text.txt and writes Test Service in a daily manner 
        f = open('C:test.txt', 'a')
        rc = None
        while rc != win32event.WAIT_OBJECT_0:
            f.write('Test Service  n')
            f.flush()
            # block for 24*60*60 seconds and wait for a stop event
            # it is used for a one-day loop
            rc = win32event.WaitForSingleObject(self.hWaitStop, 24 * 60 * 60 * 1000)
        f.write('shut down n')
        f.close()
if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

2 – On this step we should register our service.

Run command prompt as administrator and type as:

sc create TestService binpath= “C:Python36Python.exe c:PythonFilesAppServerSvc.py” DisplayName= “TestService” start= auto

the first argument of binpath is the path of python.exe

second argument of binpath is the path of your python file that we created already

Don’t miss that you should put one space after every “=” sign.

Then if everything is ok, you should see

[SC] CreateService SUCCESS

Now your python service is installed as windows service now. You can see it in Service Manager and registry under :

HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTestService

3- Ok now. You can start your service on service manager.

You can execute every python file that provides this service skeleton.

Answer #7:

pysc: Service Control Manager on Python

Example script to run as a service taken from pythonhosted.org:

from xmlrpc.server import SimpleXMLRPCServer
from pysc import event_stop
class TestServer:
    def echo(self, msg):
        return msg
if __name__ == '__main__':
    server = SimpleXMLRPCServer(('127.0.0.1', 9001))
    @event_stop
    def stop():
        server.server_close()
    server.register_instance(TestServer())
    server.serve_forever()

Create and start service

import os
import sys
from xmlrpc.client import ServerProxy
import pysc
if __name__ == '__main__':
    service_name = 'test_xmlrpc_server'
    script_path = os.path.join(
        os.path.dirname(__file__), 'xmlrpc_server.py'
    )
    pysc.create(
        service_name=service_name,
        cmd=[sys.executable, script_path]
    )
    pysc.start(service_name)
    client = ServerProxy('http://127.0.0.1:9001')
    print(client.echo('test scm'))

Stop and delete service

import pysc
service_name = 'test_xmlrpc_server'
pysc.stop(service_name)
pysc.delete(service_name)
pip install pysc

Answer #8:

nssm in python 3+

(I converted my .py file to .exe with pyinstaller)

nssm:
as said before

  • run nssm install {ServiceName}
  • On NSSM´s console:

    path: pathtoyourprogram.exe

    Startup directory: pathtoyour #same as the path but without your program.exe

    Arguments: empty

If you don’t want to convert your project to .exe

  • create a .bat file with python {{your python.py file name}}
  • and set the path to the .bat file

If you want to run a Python 3 application as a Windows Service, then you are going to love NSSM. Since NSSM handles the life cycle of a Windows Service for our Python 3 application, we can keep it platform agnostic.

Previously, we saw how we can use a Python 3 virtual environment in Windows 10. Given that, let’s see how we can use NSSM to run a Python 3 application as a Windows Service in its own Python 3 virtual environment.

Creating a sample Python 3 application

In order to simplify this tutorial, let’s attempt to run the Python 3 Flask application with an API endpoint that generates a QR Code image as a Windows Service.

In addition to that, let us place requirements.txt and run_app.py into the %systemdrive%%homepath%Documentsqr-code-app folder.

Contents of requirements.txt

flask-restplus==0.12.1
Pillow==6.0.0
qrcode==6.1

Contents of run_app.py

from flask import Flask, Blueprint, request, send_file
from flask_restplus import Api, Namespace, Resource, fields
# import library to help us with file IO
from io import BytesIO
 
import os, qrcode
 
# Create Flask app
app = Flask(__name__)
 
# Associate Api with Blueprint
api_blueprint = Blueprint('API', __name__)
api = Api(api_blueprint,
    title='Api for QR code app',
    version='1.0',
    description='This is an API for QR code app',
    # All API metadatas
)
 
# Create namespace for containing Qr Code related operations
qrcode_namespace = Namespace('QrCode', description='Qr code related operations')
# Specify uri of qrcode namespace as /qrcode
api.add_namespace(qrcode_namespace, path='/qrcode')
# Specify uri of api blueprint as /api
app.register_blueprint(api_blueprint, url_prefix='/api')
 
# Define input model
qrcode_creation_input = qrcode_namespace.model('QRCode creation Input', {
    'value': fields.String(required=True, description='The value that is supposed to be encoded into qrcode'),
})
 
 
# Define API endpoint for creating Qr Code image
@qrcode_namespace.route('/')
@qrcode_namespace.doc('Creates a QRCode image based on a string value.')
class QrCodeRoot(Resource):
 
    @qrcode_namespace.expect(qrcode_creation_input)
    @qrcode_namespace.produces(['image/png'])
    @qrcode_namespace.response(200, description='Return QR Code png image file.')
    @qrcode_namespace.response(400, description='Invalid input provided.')
    def post(self):
 
        # Get value to encode into QR Code
        json_input = request.get_json()
        value_to_turn_into_qrcode = json_input['value']
 
        # Create qr code image and return it as HTTP response
        pil_img = qrcode.make(value_to_turn_into_qrcode)
        img_io = BytesIO()
        pil_img.save(img_io, 'PNG')
        img_io.seek(0)
        return send_file(img_io, mimetype='image/png')
 
 
if __name__ == '__main__':
    port = int(os.getenv("PORT", "5678"))
    app.run(host='0.0.0.0', port=port)

Preparing the virtual environment to run our Python 3 application

Before continuing on, be sure to go through the tutorial on how to use a Python 3 virtual environment in Windows 10. After you had gone through the tutorial, you would have installed Python 3 on your Windows machine. In addition to that, you will understand how to create, activate and install dependencies into a virtual environment.

Creating the virtual environment to run our Python 3 application

Given these points, let us first create the virtual environment to run our Python 3 application. In order to do so, start a command prompt window and type in the following command:

python -m venv "%systemdrive%%homepath%Documentsqr-code-app-env"

After the command completes, you will find the Python 3 virtual environment within the %systemdrive%%homepath%Documentsqr-code-app-env folder.

Installing the application dependencies into the virtual environment

Next, activate your Python 3 virtual environment by running the following command:

"%systemdrive%%homepath%Documentsqr-code-app-envScriptsactivate.bat"

After your Python 3 virtual environment got activated, run the following command to install the Python dependencies for the application:

pip install -r "%systemdrive%%homepath%Documentsqr-code-apprequirements.txt"

When the dependencies are installed, you will be able to run your Python application within the virtual environment.

Creating the .bat file to run our Python 3 application in its own virtual environment

At this point in time, you are ready to create a .bat file to start your application within the virtual environment. Given that, create a run_app.bat in the %systemdrive%%homepath%Documentsqr-code-app folder with the following content:

call ..qr-code-app-envScriptsactivate.bat
call python run_app.py

Downloading a copy of NSSM

After you had created the windows batch file to run the your Python application within the virtual environment, proceed to download NSSM. Save the .zip file and extract its content. After extracting the content, you will find a nssm.exe file inside the win32 and win64 folder.

Depending on whether your Windows is 32 bit or 64 bit, you will use the corresponding nssm.exe file to install your Python application as a Windows Service.

For the purpose of this tutorial, let’s assume that we want to use the nssm.exe in the C:nssm-2.24win64 folder.

Run a command prompt instance as administrator

In order to avoid being prompted for administrator access for subsequent calls to C:nssm-2.24win64, let’s run a command prompt instance as administrator.

Finding Command Prompt and running it as administrator on Windows 10

We will run the subsequent commands in this command prompt instance.

Installing your Python application as a Windows Service with nssm.exe

Given that our nssm.exe is located in C:nssm-2.24win64, we can then use the following command to install our Python application as a Windows Service:

C:nssm-2.24win64nssm install qrcodeapp "%systemdrive%%homepath%Documentsqr-code-apprun_app.bat"

When you run the command, NSSM will install your Python 3 application as a Windows Service.

At this point in time, your Windows Service is not yet started.

Enabling log files for your Python 3 application

When you start your Windows Service at this point in time, it will not generate any log files when it runs.

Therefore, let’s configure it to channel application output to log files.

In order to do so, first start NSSM service editor with the following command:

C:nssm-2.24win64nssm edit qrcodeapp

After the command run, NSSM service editor will appear for enabling log files for your Python 3 application.

nssm-2.24 service editor for editing qrcodeapp written in Python 3 Flask

Given that, click on the I/O tab and enter the paths that you want the log files to appear:

nssm-2.24 service editor for qrcodeapp with output and error log paths set

Once you are done with that, click Edit service.

Starting your Python application as a Windows Service

In order to start the Windows Service for your Python application, run the following command:

C:nssm-2.24win64nssm start qrcodeapp

Since Startup Type for the Windows Service is set to Automatic, our Python 3 Flask application will run automatically when we had started our Windows machine.

Further usages of NSSM

At this point in time, we will have fulfilled our goal of using NSSM to run a Python 3 application as a Windows Service in its own Python 3 virtual environment.

In addition to what I had covered, the nssm.exe contains several other functionalities.

In case you need to use other functionalities of NSSM, you can refer to NSSM usage page for more information.

Понравилась статья? Поделить с друзьями:
  • Windows run python from command line
  • Windows run out of memory utorrent что делать
  • Windows run out of memory utorrent при создании
  • Windows run out of memory unable to allocate utorrent
  • Windows run out of memory unable to allocate 32768000 bytes