Reputation: 865
PyInstaller 3.4
Python 3.7
Win 7-64 bit
I made a .exe
from python code using PyInstaller
that has the following logic. The goal is to launch the http server in a new console window on Windows. This is to avoid locking up/blocking the main application and the user can just close the newly launched console window when they are finished with the http server.
p = subprocess.call('start "" python -m http.server --directory {} {} --bind {}'.format(web_server_path, web_server_port, web_bind_ip), shell=True)
This works on my windows PC that has Python installed but naturally does not work on a PC without Python.
Since the goal is to distribute this .exe
to people without a python installation, can someone recommend the correct method to accomplish the above behavior using Python within PyInstaller? I know that a PyInstaller generated .exe
is essentially running a Python interpreter but I don't know how to tap into it with my code.
UPDATE
It is a requirement that the user be able to set the directory
, port
, and address
. It should also be easy to stop ideally by just closing a window (hence the original command line method) but that is open to change.
FURTHER UPDATE
Is it possible to load the python3.dll and use its function directly somehow?
ctypes.WinDLL('Path:\to\my.dll')
Upvotes: 1
Views: 233
Reputation: 31379
Since your example code would presumably start the web server without blocking the further execution of your script, you'd want to run the web server on a separate thread.
from sys import version_info
import http.server
import socketserver
from threading import Thread
class WebServer(Thread):
@staticmethod
def get_directory_simple_http_request_handler(directory):
_current_only = version_info < (3, 7)
class _DirectorySimpleHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
_current_only = version_info < (3, 7)
def __init__(self, *args, **kwargs):
if not self._current_only:
kwargs['directory'] = directory
super(_DirectorySimpleHTTPRequestHandler, self).__init__(*args, **kwargs)
@property
def current_only(self):
return self._current_only
return _DirectorySimpleHTTPRequestHandler
def __init__(self, address='127.0.0.1', port=8000, directory=''):
self.handler = self.get_directory_simple_http_request_handler(directory)
self.httpd = socketserver.TCPServer((address, port), self.handler)
Thread.__init__(self)
def run(self):
self.httpd.serve_forever()
def join(self, *args, **kwargs):
self.httpd.shutdown()
super().join(*args, **kwargs)
web_server = None
while True:
if web_server is None:
print('Currently not running (type start or exit).')
else:
print('Running on:', web_server.httpd.server_address, '(type stop or exit)')
if web_server.handler.current_only:
print('Serving current directory, Python <3.7 has no `directory` on SimpleHTTPRequestHandler.')
command = input('>')
if command == 'start':
if web_server is None:
web_server = WebServer(directory=r'C:\Temp')
web_server.start()
if command == 'stop':
if web_server is not None:
web_server.join()
web_server = None
if command == 'exit':
exit(0)
Note that Python 3.7 is the first version to support the directory
parameter, earlier versions of Python will only serve the current directory for the process.
The factory static method is there to set up a separate instance of the SimpleHTTPRequestHandler
which gets initialised with the desired directory.
If you prefer to actually just launch a console window, running the Python (test) web server (or another external server, if Python is not available):
import time
from subprocess import Popen, CREATE_NEW_CONSOLE
proc = Popen(['python', '-m', 'http.server', '8000'],
cwd=r'C:\temp',
creationflags=CREATE_NEW_CONSOLE, close_fds=True)
while True:
print('Running')
time.sleep(2)
Similarly, if you like it quiet, but don't need access to the web server object, this also works:
import time
from subprocess import Popen
proc = Popen(['python', '-m', 'http.server', '8000'], close_fds=True, cwd=r'C:\temp')
while True:
print('Running')
time.sleep(2)
Upvotes: 1