141

this is my Python3 project hiearchy:

projet
  \
  script.py
  web
    \
    index.html

From script.py, I would like to run a http server which serve the content of the web folder.

Here is suggested this code to run a simple http server:

import http.server
import socketserver

PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("", PORT), Handler)
print("serving at port", PORT)
httpd.serve_forever()

but this actually serve project, not web. How can I specify the path of the folder I want to serve?

3
  • 13
    Have you considered running python3 -m http.server -d /path/to/web/dir on command line to do the job? Props to @kyle-barron who gave this perfect solution in a comment deep below. Commented Mar 7, 2020 at 20:19
  • I suggest this is the best answer: stackoverflow.com/a/58217918/835098 Commented Mar 9, 2020 at 19:11
  • @AkseliPalén I get server.py: error: argument port: invalid int value: '/media/EHD/web_root' error when I run that in Ubuntu 18.04 Terminal (with python3.6.9) Commented Jul 10, 2021 at 11:10

7 Answers 7

123

In Python 3.7 SimpleHTTPRequestHandler can take a directory argument:

import http.server
import socketserver

PORT = 8000
DIRECTORY = "web"


class Handler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, directory=DIRECTORY, **kwargs)


with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()

and from the command line:

python -m http.server --directory web

To get a little crazy... you could make handlers for arbitrary directories:

def handler_from(directory):
    def _init(self, *args, **kwargs):
        return http.server.SimpleHTTPRequestHandler.__init__(self, *args, directory=self.directory, **kwargs)
    return type(f'HandlerFrom<{directory}>',
                (http.server.SimpleHTTPRequestHandler,),
                {'__init__': _init, 'directory': directory})


with socketserver.TCPServer(("", PORT), handler_from("web")) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()
Sign up to request clarification or add additional context in comments.

4 Comments

Better to use http.server.HTTPServer (in exactly the same way as socketserver.TCPServer)
Note that if you're calling this from a Windows shortcut, then the "Start in" property of the shortcut will define what's the current working directory where web will be searched for. That is, if the command line of your Windows shortcut is C:\Windows\System32\cmd.exe /k "python -m http.server --directory web", then in the shortcut properties, make sure to leave the "Start in" field empty so that it'll consider web as relative to the path of the shortcut file, rather than \System32\cmd.exe.
As @Andrew said, this answer would be better if it would use the subclass http.server.HTTPServer instead of socketserver.TCPServer. One important reason is that http.server.HTTPServer uses SO_REUSEADDR, thus avoiding the chronic "Address already in use" problems when the server is killed and then restarted. That problem makes socketserver.TCPServer by itself a low-quality tool.
100

If you just want serve static file you can do it by running SimpleHTTPServer module using python 2:

 python -m SimpleHTTPServer

Or with python 3:

 python3 -m http.server

This way you do not need to write any script.

7 Comments

Note that you can also add a port at the end to specify the port of the server.
further note; you have to have an index.html or index.htm file in the directory you're running the server from in order for it to be served; otherwise you get a directory list.
OP asks how to run it at specific folder path, not from present working directory.
There's a directory argument to http.server. So you can do python3 -m http.server -d /path/to/web/dir
While this answer was accepted by the owner of the question as being the best answer, I think it should not be the best answer. The question was asking for a way how to code this in python and not how to invoke it from the command line.
|
53

https://docs.python.org/3/library/http.server.html#http.server.SimpleHTTPRequestHandler

This class serves files from the current directory and below, directly mapping the directory structure to HTTP requests.

So you just need to change the current directory prior to starting the server - see os.chdir

eg:

import http.server
import socketserver
import os

PORT = 8000

web_dir = os.path.join(os.path.dirname(__file__), 'web')
os.chdir(web_dir)

Handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("", PORT), Handler)
print("serving at port", PORT)
httpd.serve_forever()

4 Comments

Thank you! Note: I added try: httpd.serve_forever(); except KeyboardInterrupt: pass; httpd.server_close() to actually close the port.
Thanks. Its worked. But how can I get the Host address for this? I mean to say that how can I get 0.0.0.0:8000 instead of my local router address 192.168.0.100:8000
@roipoussiere You rather should use with socketserver.TCPServer(("", PORT), Handler) as httpd:, this way it closes automatically after being used
For future readers: Andy Hayden's solution below is less 'hacky', and probably preferred, since it doesn't rely on side-effects.
36

You also can run the command line with

python3 -m http.server -d web 8000

2 Comments

or to see all supported parameters try python3 -m http.server --help
I had to add -d with current/desired folder to make it work with my html: python3 -m http.server -d web 8000 -d .
30

There's a shorter method for Python 3+:

import functools
    
Handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory='/my/dir/goes/here')

4 Comments

this is a very cool way to do this and it works for me. I'd definitively recommend this as the best answer!
TypeError: __init__() got an unexpected keyword argument 'directory'
I get the same error in Python 2.7. It worked for me on Python 3.8.
Stop using Python 2 :)
21

Just for completeness, here's how you can setup the actual server classes to serve files from an arbitrary directory:

try
    # python 2
    from SimpleHTTPServer import SimpleHTTPRequestHandler
    from BaseHTTPServer import HTTPServer as BaseHTTPServer
except ImportError:
    # python 3
    from http.server import HTTPServer as BaseHTTPServer, SimpleHTTPRequestHandler


class HTTPHandler(SimpleHTTPRequestHandler):
    """This handler uses server.base_path instead of always using os.getcwd()"""
    def translate_path(self, path):
        path = SimpleHTTPRequestHandler.translate_path(self, path)
        relpath = os.path.relpath(path, os.getcwd())
        fullpath = os.path.join(self.server.base_path, relpath)
        return fullpath


class HTTPServer(BaseHTTPServer):
    """The main server, you pass in base_path which is the path you want to serve requests from"""
    def __init__(self, base_path, server_address, RequestHandlerClass=HTTPHandler):
        self.base_path = base_path
        BaseHTTPServer.__init__(self, server_address, RequestHandlerClass)

Then you can set any arbitrary path in your code:

web_dir = os.path.join(os.path.dirname(__file__), 'web')
httpd = HTTPServer(web_dir, ("", 8000))
httpd.serve_forever()

Comments

10

Another easy method to serve from a specific directory.

Since you really only need to set the directory parameter for the SimpleHTTPRequestHandler, you can use functools.partial to prepare the handler class without instantiating the class.

from functools import partial
from http.server import HTTPServer, SimpleHTTPRequestHandler
from pathlib import Path


def start_httpd(directory: Path, port: int = 8000):
    print(f"serving from {directory}...")
    handler = partial(SimpleHTTPRequestHandler, directory=directory)
    httpd = HTTPServer(('localhost', port), handler)
    httpd.serve_forever()
    

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.