below is my code that I am trying to turn into a windows service. You'll see test.py as the call it makes and all this is a short script that writes into a log file (as a test).
The code is there to make it a windows service and it does that fine, but when I run it nothing writes into the log file. Help greatly appreciated. Below is the code:
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil
import os, sys, string, time
class aservice(win32serviceutil.ServiceFramework):
_svc_name_ = "MyServiceShortName"
_svc_display_name_ = "A python test"
_svc_description_ = "Writing to a log"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
import servicemanager
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, ''))
self.timeout = 1000 #1 seconds
# This is how long the service will wait to run / refresh itself (see script below)
while 1:
# Wait for service stop signal, if I timeout, loop again
rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
# Check to see if self.hWaitStop happened
if rc == win32event.WAIT_OBJECT_0:
# Stop signal encountered
servicemanager.LogInfoMsg("SomeShortNameVersion - STOPPED!") #For Event Log
break
else:
#what to run
try:
file_path = "test.py"
execfile(file_path)
except:
pass
#end of what to run
def ctrlHandler(ctrlType):
return True
if __name__ == '__main__':
win32api.SetConsoleCtrlHandler(ctrlHandler, True)
win32serviceutil.HandleCommandLine(aservice)
Edit: Thought for the sake of it I'd include my test.py file's code, it has unnecessary imports but will get the job done if you run it alone.
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil
import os
logfile = open("log.txt", "a") #open file to log restart timestamp
logfile.write("\nthat's good!!!!")
logfile.close()
Okay so I figured it out and would like to come back and post in case anyone else is dealing with this, all though this was a tad unique.
You have to specify your file path if you are within a windows service, duh... but it wasn't done and I pulled my hair out for no reason.
file_path = "test.py"
should have been
file_path = r"c:\users\...\test.py"
Be careful when using '\' for windows file paths. They must be escaped like '\\' or the string must be declared as raw string ('r'). Using the unix like slash '/' separators works also but may look odd for windows users.
Related
I have a Django app that I'm trying to run via Cherrypy on Windows Server. I have below code in the service creation script.
import os
import sys
service_directory = os.path.dirname(__file__)
source_directory = os.path.abspath(service_directory)
os.chdir(source_directory)
venv_base = os.path.abspath(os.path.join(source_directory, ".venv"))
print(venv_base)
sys.path.append(".")
old_os_path = os.environ['PATH']
os.environ['PATH'] = os.path.join(venv_base, "Scripts")+ os.pathsep + old_os_path
site_packages = os.path.join(venv_base, "Lib", "site-packages")
prev_sys_path = list(sys.path)
import site
site.addsitedir(site_packages)
sys.real_prefix = sys.prefix
sys.prefix = venv_base
new_sys_path = list()
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
import pathlib
import subprocess
import traceback
import servicemanager
import win32event
import win32service
import win32serviceutil
from win32api import SetConsoleCtrlHandler
class AppServerSvc(win32serviceutil.ServiceFramework):
"""
Service Create class
"""
_svc_name_ = "MyService" # service name
_svc_display_name_ = "MyService" # display name
def __init__(self, args):
"""
Constructor
"""
win32serviceutil.ServiceFramework.__init__(self, args)
SetConsoleCtrlHandler(lambda x: True, True)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
"""
Function to stop the windows service
"""
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
self.proc.terminate()
def SvcDoRun(self):
"""
Function to create and start the windows service
"""
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
try:
self.main()
except Exception as exp_msg:
servicemanager.LogErrorMsg(traceback.format_exc())
os._exit(-1)
def main(self):
"""
Script to run as the windows service
"""
#env_file.load(f'{os.path.join(pathlib.Path(__file__).parent.absolute(), "config.env")}')
#settings.set_env_variables()
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
self.proc = subprocess.Popen(f"python"
f" {os.path.join(pathlib.Path(__file__).parent.absolute(), 'cherryd.py')} 1"
, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
stdin=subprocess.DEVNULL)
stdout, stderr = self.proc.communicate()
print(stdout)
if (self.proc.poll() == 0):
servicemanager.LogMsg(f"Successfully started the process with PID: {self.proc.pid}")
else:
servicemanager.LogErrorMsg(f"Failed to start the services")
if __name__ == '__main__':
# Start of the process
win32serviceutil.HandleCommandLine(AppServerSvc)
I'm able to install this service using python service.py install, no issues here.
When I start the service python service.py start I get error Error starting service: The service did not respond to the start or control request in a timely fashion.
When I do python service.py debug, everything works normal and I'm able to access the app using FQDN. Why does the service not work when started normally and work only during debug mode.
Have read multiple posts on similar errors but nothing helped. I'm using Python 3.8 and have added the venv Scripts directory to PATH as well. Can someone please help.
Update
Started the service with my user account and it worked, still don't understand how it worked in debug mode.
I was able to create a python service on windows 10 using this example: Is it possible to write a windows (10) service with python (standard python.org python) without compiling
Then I created a separate python file using mss to take a single screenshot of monitor 1. Everything worked fine when I run them separately. However, when i replace the text file code in the def main(self) with the screenshot code, the resulting image file is just black.
Feel like the monitor number isn't being accessed in the service, but not sure why or even if that is the issue.
Edit: The image isn't blank it's black. Updated above from blank to black.
import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
import mss
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
with mss.mss() as sct:
filename = sct.shot(output="C:\\Output\\mon-{mon}-.png")
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(AppServerSvc)
I programmed a gateway to a opcua-server with python-opcua.
The gateway is subscribing some values in the opcua. That is working good and fast.
Now I want to call a script that writes to the opcua.
In principle, it works too. But because I have to import the whole gateway(and all opcua stuff), it is very slow...
My Question: Is is possible to trigger a function in my class-instance without imorting everything?
To start e.g. function setBool(), I have to import Gateway...
#!/usr/bin/env python3.5 -u
# -*- coding: utf-8 -*-
import time
import sys
import logging
from logging.handlers import RotatingFileHandler
from threading import Thread
from opcua import Client
from opcua import ua
from subscribeOpcua import SubscribeOpcua
from cmdHandling import CmdHandling
from keepConnected import KeepConnected
class Gateway(object):
def __init__(self):
OPCUA_IP = '1.25.222.222'
OPCUA_PORT = '4840'
OPCUA_URL = "opc.tcp://{}:{}".format(OPCUA_IP, str(OPCUA_PORT))
addr = "OPCUA-URL:{}.".format(OPCUA_URL)
# Setting up opcua-handler
self.client = Client(OPCUA_URL)
self.opcuaHandlers = [SubscribeOpcua()]
# Connect to opcua
self.connecter = KeepConnected(self.client,self.opcuaHandlers)
self.connecter.start()
def setBool(self, client):
"""Set e boolean variable on opcua-server.
"""
path = ["0:Objects","2:DeviceSet"...]
root = client.get_root_node()
cmd2opcua = root.get_child(path)
cmd2opcua.set_value(True)
if __name__ == "__main__":
"""Open connecter when gateway is opened directly.
"""
connect = Gateway()
The only way to prevent a code from runing when importing a module is to put it inside a method:
def import_first_part():
global re
global defaultdict
print('import this first part')
# import happen locally
# because when you do `import re` actually
# re = __import__('re')
import re
from collections import defaultdict
def import_second_part():
print('import pandas')
# really unnecessary check here because if we import
# pandas for the second time it will just retrieve the object of module
# the code of module is executed only in the first import in life of application.
if 'pandas' in globals():
return
global pandas
import pandas
def use_regex():
import_first_part()
# do something here
if __name__ == '__main__':
use_regex()
re.search('x', 'xb') # works fine
I checked that 'pandas' is in global scope before reimport it again but really this is not necessary, because when you import a module for the second time it's just retrieved no heavy calculation again.
I'm writing a Windows service with multiproccesing and I'm running into problems with the method that's called in the pool.
Right now I'm able to install the service and run it, it outputs The service started running... to the log file but nothing else.
Looking at the process explorer (see screenshot below), I see that the processes are being created and finishing constantly, but the code within the TestMethod isn't being run, and the service isn't exiting the pool because nothing else is being written to the file.
I'm not able to stop the service since it's stuck in the pool and doesn't reach the check for the stop event.
Why is the code within the TestMethod not running at all?
Service code:
import servicemanager
import win32event
import win32service
import win32serviceutil
import multiprocessing
class TestService(win32serviceutil.ServiceFramework):
_svc_name_ = "TestService"
_svc_display_name_ = "Test Service"
def testMethod(self, testVar):
with open('C:\\Test.log', 'a') as f:
f.write('The method is running: ' + testVar)
f.close()
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):
with open('C:\\Test.log', 'a') as f:
f.write('The service started running...\n')
f.close()
rc = None
p = multiprocessing.Pool(5)
p.map(TestService.testMethod, range(1,6))
with open('C:\\Test.log', 'a') as f:
f.write('Finished method...\n')
f.close()
while rc != win32event.WAIT_OBJECT_0:
with open('C:\\Test.log', 'a') as f:
f.write('The service is running...\n')
f.close()
rc = win32event.WaitForSingleObject(self.hWaitStop, 5000)
with open('C:\\Test.log', 'a') as f:
f.write('StreamCapture service stopped.\n')
f.close()
if __name__ == '__main__':
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(TestService)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(TestService)
There are two problems in your code:
you point map to TestService.testMethod, which is an "unbound function" that lives in the Class namespace, but testMethod is defined like a class method. You either need to call it with self.testMethod or you remove the self from the function definition
you try to add an int to a string, use f.write('The method is running: {}'.format(testVar)) instead
Your stripped down program, corrected would look like this:
import multiprocessing
class TestService:
def testMethod(self, testVar):
with open('C:\\Test.log', 'a') as f:
f.write('The method is running: {}'.format(testVar))
f.close()
def SvcDoRun(self):
p = multiprocessing.Pool(5)
p.map(self.testMethod, range(1,6))
if __name__ == "__main__":
d = TestService()
d.SvcDoRun()
P.S. try to post a minimal code example the next time: Strip down the code to the bare minimum which generates the error. The code fragment I posted would have been enough to explain the problem. This way it is easier to understand for readers and you get an answer faster.
The problem was caused by a known issue with pyinstaller and onefile executables on Windows.
Adding the following try block after my imports fixed it for me:
try:
# Python 3.4+
if sys.platform.startswith('win'):
import multiprocessing.popen_spawn_win32 as forking
else:
import multiprocessing.popen_fork as forking
except ImportError:
import multiprocessing.forking as forking
See https://github.com/pyinstaller/pyinstaller/wiki/Recipe-Multiprocessing for more details
I coded out a windows service in python to write some text to a file continuously and installed it and ran it and it works fine. Now if I try to convert my python windows service script into an executable (.exe) using py2exe. The .exe installs fine as a service but when I try to start it I get the error "The server did not respond to the start ......in timely fashion". Is this got something to do with py2exe destroying information in my python script. How do I go around this? (I am trying to convert it to a .exe because I want to distribute it).
My python script is as follows:
import win32service
import win32serviceutil
import win32event
class clear_queue(win32serviceutil.ServiceFramework):
_svc_name_ = "avant"
_svc_display_name_ = "avant"
_svc_description_ = "Elegant file writer"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self,args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcDoRun(self):
import servicemanager;
fil = open("C:/Users/u/Desktop/c99/user.txt",'r+');
rc = win32event.WaitForSingleObject(self.hWaitStop, 64)
while rc != win32event.WAIT_OBJECT_0:
fil.write("george\n");
rc = win32event.WaitForSingleObject(self.hWaitStop, 64)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(clear_queue)
Looking at the example at http://tools.cherrypy.org/wiki/WindowsService, it looks like you need to add self.ReportServiceStatus(win32service.SERVICE_STOPPED) as the final line of the SvcStop method.