Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Doc/library/http.server.rst
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ provides three different variants:

If not specified, the directory to serve is the current working directory.

.. versionchanged:: 3.9
Accepts a :term:`path-like object`.

The :class:`SimpleHTTPRequestHandler` class defines the following methods:

.. method:: do_HEAD()
Expand Down
2 changes: 1 addition & 1 deletion Lib/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def __init__(self, *args, directory=None, **kwargs):
if directory is None:
directory = os.getcwd()
self.directory = directory
self.directory = os.fspath(directory)
super().__init__(*args, **kwargs)

def do_GET(self):
Expand Down
97 changes: 74 additions & 23 deletions Lib/test/test_httpservers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import re
import base64
import ntpath
import pathlib
import shutil
import email.message
import email.utils
Expand Down Expand Up @@ -789,10 +790,10 @@ def test_query_with_continuous_slashes(self):


class SocketlessRequestHandler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
def __init__(self, directory=None):
request = mock.Mock()
request.makefile.return_value = BytesIO()
super().__init__(request, None, None)
super().__init__(request, None, None, directory=directory)

self.get_called = False
self.protocol_version = "HTTP/1.1"
Expand Down Expand Up @@ -1067,41 +1068,91 @@ def test_date_time_string(self):
class SimpleHTTPRequestHandlerTestCase(unittest.TestCase):
""" Test url parsing """
def setUp(self):
self.translated = os.getcwd()
self.translated = os.path.join(self.translated, 'filename')
self.handler = SocketlessRequestHandler()
self.translated_1 = os.path.join(os.getcwd(), 'filename')
self.translated_2 = os.path.join('foo', 'filename')
self.translated_3 = os.path.join('bar', 'filename')
self.handler_1 = SocketlessRequestHandler()
self.handler_2 = SocketlessRequestHandler(directory='foo')
self.handler_3 = SocketlessRequestHandler(directory=pathlib.PurePath('bar'))

def test_query_arguments(self):
path = self.handler.translate_path('/filename')
self.assertEqual(path, self.translated)
path = self.handler.translate_path('/filename?foo=bar')
self.assertEqual(path, self.translated)
path = self.handler.translate_path('/filename?a=b&spam=eggs#zot')
self.assertEqual(path, self.translated)
path = self.handler_1.translate_path('/filename')
self.assertEqual(path, self.translated_1)
path = self.handler_2.translate_path('/filename')
self.assertEqual(path, self.translated_2)
path = self.handler_3.translate_path('/filename')
self.assertEqual(path, self.translated_3)

path = self.handler_1.translate_path('/filename?foo=bar')
self.assertEqual(path, self.translated_1)
path = self.handler_2.translate_path('/filename?foo=bar')
self.assertEqual(path, self.translated_2)
path = self.handler_3.translate_path('/filename?foo=bar')
self.assertEqual(path, self.translated_3)

path = self.handler_1.translate_path('/filename?a=b&spam=eggs#zot')
self.assertEqual(path, self.translated_1)
path = self.handler_2.translate_path('/filename?a=b&spam=eggs#zot')
self.assertEqual(path, self.translated_2)
path = self.handler_3.translate_path('/filename?a=b&spam=eggs#zot')
self.assertEqual(path, self.translated_3)

def test_start_with_double_slash(self):
path = self.handler.translate_path('//filename')
self.assertEqual(path, self.translated)
path = self.handler.translate_path('//filename?foo=bar')
self.assertEqual(path, self.translated)
path = self.handler_1.translate_path('//filename')
self.assertEqual(path, self.translated_1)
path = self.handler_2.translate_path('//filename')
self.assertEqual(path, self.translated_2)
path = self.handler_3.translate_path('//filename')
self.assertEqual(path, self.translated_3)

path = self.handler_1.translate_path('//filename?foo=bar')
self.assertEqual(path, self.translated_1)
path = self.handler_2.translate_path('//filename?foo=bar')
self.assertEqual(path, self.translated_2)
path = self.handler_3.translate_path('//filename?foo=bar')
self.assertEqual(path, self.translated_3)

def test_windows_colon(self):
with support.swap_attr(server.os, 'path', ntpath):
path = self.handler.translate_path('c:c:c:foo/filename')
path = self.handler_1.translate_path('c:c:c:foo/filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated_1)
path = self.handler_2.translate_path('c:c:c:foo/filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated_2)
path = self.handler_3.translate_path('c:c:c:foo/filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated)
self.assertEqual(path, self.translated_3)

path = self.handler.translate_path('\\c:../filename')
path = self.handler_1.translate_path('\\c:../filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated_1)
path = self.handler_2.translate_path('\\c:../filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated_2)
path = self.handler_3.translate_path('\\c:../filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated)
self.assertEqual(path, self.translated_3)

path = self.handler.translate_path('c:\\c:..\\foo/filename')
path = self.handler_1.translate_path('c:\\c:..\\foo/filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated)
self.assertEqual(path, self.translated_1)
path = self.handler_2.translate_path('c:\\c:..\\foo/filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated_2)
path = self.handler_3.translate_path('c:\\c:..\\foo/filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated_3)

path = self.handler.translate_path('c:c:foo\\c:c:bar/filename')
path = self.handler_1.translate_path('c:c:foo\\c:c:bar/filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated_1)
path = self.handler_2.translate_path('c:c:foo\\c:c:bar/filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated_2)
path = self.handler_3.translate_path('c:c:foo\\c:c:bar/filename')
path = path.replace(ntpath.sep, os.sep)
self.assertEqual(path, self.translated)
self.assertEqual(path, self.translated_3)


class MiscTestCase(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Allow passing a :term:`path-like object` as ``directory`` argument to the
:class:`http.server.SimpleHTTPRequestHandler` class. Patch by Géry Ogam.