From b31d8ac1110d95afd6b804b88f8988c589110fbb Mon Sep 17 00:00:00 2001 From: Doncho Gunchev Date: Mon, 2 Nov 2020 00:01:32 +0200 Subject: [PATCH 01/20] Handle invalid/missing Content-Length header in POST. --- httpdecho.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/httpdecho.py b/httpdecho.py index e01a548..4be35ae 100644 --- a/httpdecho.py +++ b/httpdecho.py @@ -47,10 +47,15 @@ def do_POST(self): Echo a request with a body. """ message = self.get_message() - message.set_payload(self.rfile.read( - int(self.headers['Content-Length']))) - self.send_head() - BytesGenerator(self.wfile).flatten(message, unixfrom=False) + try: + length = int(self.headers['Content-Length']) + except (TypeError, ValueError) as exc: + message.set_payload("Invalid Content-Length: " + str(exc)) + else: + message.set_payload(self.rfile.read(length)) + finally: + self.send_head() + BytesGenerator(self.wfile).flatten(message, unixfrom=False) do_PUT = do_POST do_PATCH = do_POST From 2987b18c78d1437c1d267a9d25766ad9d496a33f Mon Sep 17 00:00:00 2001 From: Doncho Gunchev Date: Mon, 2 Nov 2020 00:17:50 +0200 Subject: [PATCH 02/20] Ignore more generated files --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e54b6a3..bcabcd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ /.eggs/ /.tox/ -/dist/ \ No newline at end of file +/dist/ +/httpd_echo.egg-info/ +/__pycache__/ +*.pyc +*.pyo From 8cb641bc1c324ac38f804aca1a2ea6ec593858c4 Mon Sep 17 00:00:00 2001 From: Doncho Gunchev Date: Mon, 2 Nov 2020 00:18:37 +0200 Subject: [PATCH 03/20] Add tests for python 3.6 to 3.9 too --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 76e3b16..ccb5698 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py35 +envlist = py27,py35,py36,py37,py38,py39 [testenv] commands = python setup.py test From a18ebcec44fc694399a96aadd0a00ea79ff9d4e8 Mon Sep 17 00:00:00 2001 From: Doncho Gunchev Date: Mon, 2 Nov 2020 00:19:40 +0200 Subject: [PATCH 04/20] Cosmetics - missing new line and inconsistent indent --- README.rst | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 96c1eac..122853b 100644 --- a/README.rst +++ b/README.rst @@ -79,4 +79,4 @@ ____________________________ ``Content-Type`` and ``Accept`` support for content negotiation: Return the response body in the format specified in the ``Accept`` header if - given, otherwise in the same ``Content-Type`` as the request. \ No newline at end of file + given, otherwise in the same ``Content-Type`` as the request. diff --git a/setup.py b/setup.py index d6241ad..d28f596 100644 --- a/setup.py +++ b/setup.py @@ -39,4 +39,4 @@ extras_require=dict(test=test_requires), test_suite='tests', entry_points=dict(console_scripts=['httpd-echo=httpdecho:main']), - ) +) From 2b048b5ce9fd498f2895dad75fdb9bdba70044f4 Mon Sep 17 00:00:00 2001 From: Doncho Gunchev Date: Mon, 2 Nov 2020 00:44:36 +0200 Subject: [PATCH 05/20] Import argparse only in main(), v0.1.1 --- httpdecho.py | 22 +++++++++++----------- setup.py | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/httpdecho.py b/httpdecho.py index 4be35ae..21d9cf2 100644 --- a/httpdecho.py +++ b/httpdecho.py @@ -3,7 +3,6 @@ """ import socket -import argparse from six.moves.urllib import parse import email.message try: @@ -14,16 +13,6 @@ from six.moves import BaseHTTPServer -parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.ArgumentDefaultsHelpFormatter) -parser.add_argument( - '--address', '-a', default='localhost', - help='Hostname or IP address to accept requests on.') -parser.add_argument( - '--port', '-p', help='Port to accept requests on. ' - 'If not specified, use the first available port after 8000.') - class EchoHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): """ @@ -90,6 +79,17 @@ def main(args=None, default_port=8000): """ Run the echo HTTP server. """ + import argparse + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument( + '--address', '-a', default='localhost', + help='Hostname or IP address to accept requests on.') + parser.add_argument( + '--port', '-p', help='Port to accept requests on. ' + 'If not specified, use the first available port after 8000.') + args = parser.parse_args(args) port = args.port diff --git a/setup.py b/setup.py index d28f596..4d2371a 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages import os -version = '0.1' +version = '0.1.1' test_requires = ['requests'] From 0eaaffbca2b373c9cde226314319da0588daa20b Mon Sep 17 00:00:00 2001 From: Doncho Gunchev Date: Mon, 2 Nov 2020 00:45:35 +0200 Subject: [PATCH 06/20] Eclipse PyDev specific .gitignore settings. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index bcabcd9..cf39ccc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ /__pycache__/ *.pyc *.pyo +/.project +/.pydevproject From 03e7caae316e87bbcba2b9cdf9b2b6b17b0bfbb9 Mon Sep 17 00:00:00 2001 From: Doncho Gunchev Date: Tue, 5 Jan 2021 16:36:27 +0200 Subject: [PATCH 07/20] Add shebang and encoding --- httpdecho.py | 3 +++ setup.py | 3 +++ tests.py | 2 ++ 3 files changed, 8 insertions(+) mode change 100644 => 100755 httpdecho.py mode change 100644 => 100755 setup.py diff --git a/httpdecho.py b/httpdecho.py old mode 100644 new mode 100755 index 21d9cf2..e8eef62 --- a/httpdecho.py +++ b/httpdecho.py @@ -1,3 +1,6 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + """ A Simple Python HTTP server that echos the request in the response. """ diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 4d2371a..fab82f8 --- a/setup.py +++ b/setup.py @@ -1,3 +1,6 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + from setuptools import setup, find_packages import os diff --git a/tests.py b/tests.py index e61b45c..58f31c3 100644 --- a/tests.py +++ b/tests.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + """ Test the echo HTTP server. """ From c8846550315341049373a3ffc09300d9a2a5b820 Mon Sep 17 00:00:00 2001 From: Doncho Gunchev Date: Tue, 5 Jan 2021 16:37:52 +0200 Subject: [PATCH 08/20] Fix for `httpdecho.py` missing from pipy #1 https://github.com/rpatterson/httpd-echo/issues/1 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index fab82f8..5a3a55a 100755 --- a/setup.py +++ b/setup.py @@ -33,6 +33,7 @@ license='GPL', packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), include_package_data=True, + py_modules=['httpdecho'], zip_safe=False, install_requires=[ # -*- Extra requirements: -*- From 77c2fcab1aa4629f0e5f15734d8ad49a8749252a Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Tue, 22 Feb 2022 23:44:32 +0200 Subject: [PATCH 09/20] Update the list of environments to test in tox. Signed-off-by: Doncho N. Gunchev --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index ccb5698..b3880d1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py35,py36,py37,py38,py39 +envlist = py27,py3{6,7,8,9,10},pypy{2,3} [testenv] commands = python setup.py test From 1f38984b7fb33c9b73693e97eac69a9ffc5e4e5a Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Wed, 13 Jul 2022 12:05:45 +0300 Subject: [PATCH 10/20] cleanup --- httpdecho.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/httpdecho.py b/httpdecho.py index e8eef62..0434ecf 100755 --- a/httpdecho.py +++ b/httpdecho.py @@ -6,23 +6,25 @@ """ import socket -from six.moves.urllib import parse import email.message +from six.moves.urllib import parse try: from email.generator import BytesGenerator except ImportError: # BBB Python 2 compatibility from email.generator import Generator as BytesGenerator - from six.moves import BaseHTTPServer +__all__ = ['EchoHTTPRequestHandler'] + + class EchoHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): """ A Simple Python HTTP server that echos the request in the response. """ - def do_GET(self): + def do_GET(self): # pylint: disable=invalid-name """ Echo a request without a body. """ @@ -34,7 +36,7 @@ def do_GET(self): do_OPTIONS = do_GET do_DELETE = do_GET - def do_POST(self): + def do_POST(self): # pylint: disable=invalid-name """ Echo a request with a body. """ @@ -82,7 +84,7 @@ def main(args=None, default_port=8000): """ Run the echo HTTP server. """ - import argparse + import argparse # pylint: disable=import-outside-toplevel parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -106,7 +108,8 @@ def main(args=None, default_port=8000): except socket.error: port += 1 if port > 65535: - raise ValueError('No available port found') + # Silence pylint to keep it python2 compatible. + raise ValueError('No available port found') # pylint: disable=raise-missing-from else: bound = True else: From a31f8db4b49998349499600c3e5beff7fcc9eb61 Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Wed, 13 Jul 2022 12:18:33 +0300 Subject: [PATCH 11/20] Version 0.2.0, add python 3.11, silence flake8. --- httpdecho.py | 14 +++++----- setup.py | 73 ++++++++++++++++++++++++++-------------------------- tox.ini | 2 +- 3 files changed, 45 insertions(+), 44 deletions(-) diff --git a/httpdecho.py b/httpdecho.py index 0434ecf..b7a14c1 100755 --- a/httpdecho.py +++ b/httpdecho.py @@ -24,7 +24,7 @@ class EchoHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): A Simple Python HTTP server that echos the request in the response. """ - def do_GET(self): # pylint: disable=invalid-name + def do_GET(self): # noqa:N802, pylint: disable=invalid-name """ Echo a request without a body. """ @@ -32,11 +32,11 @@ def do_GET(self): # pylint: disable=invalid-name self.send_head() BytesGenerator(self.wfile).flatten(message, unixfrom=False) - do_HEAD = do_GET - do_OPTIONS = do_GET - do_DELETE = do_GET + do_HEAD = do_GET # noqa:N815 + do_OPTIONS = do_GET # noqa:N815 + do_DELETE = do_GET # noqa:N815 - def do_POST(self): # pylint: disable=invalid-name + def do_POST(self): # noqa:N802, pylint: disable=invalid-name """ Echo a request with a body. """ @@ -51,8 +51,8 @@ def do_POST(self): # pylint: disable=invalid-name self.send_head() BytesGenerator(self.wfile).flatten(message, unixfrom=False) - do_PUT = do_POST - do_PATCH = do_POST + do_PUT = do_POST # noqa:N815 + do_PATCH = do_POST # noqa:N815 def send_head(self): """ diff --git a/setup.py b/setup.py index 5a3a55a..52d9783 100755 --- a/setup.py +++ b/setup.py @@ -4,43 +4,44 @@ from setuptools import setup, find_packages import os -version = '0.1.1' +version = '0.2.0' test_requires = ['requests'] -setup(name='httpd-echo', - version=version, - description=( - "A Simple Python HTTP server that echos the request in the response" - ), - long_description=open( - os.path.join(os.path.dirname(__file__), 'README.rst')).read(), - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Internet :: WWW/HTTP :: HTTP Servers', - 'Topic :: Utilities', - ], - keywords='httpd http echo server', - author='Ross Patterson', - author_email='me@rpatterson.net', - url='https://github.com/rpatterson/httpd-echo', - license='GPL', - packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), - include_package_data=True, - py_modules=['httpdecho'], - zip_safe=False, - install_requires=[ - # -*- Extra requirements: -*- - 'six', - ], - tests_require=test_requires, - extras_require=dict(test=test_requires), - test_suite='tests', - entry_points=dict(console_scripts=['httpd-echo=httpdecho:main']), +setup( + name='httpd-echo', + version=version, + description=( + "A Simple Python HTTP server that echos the request in the response" + ), + long_description=open( + os.path.join(os.path.dirname(__file__), 'README.rst')).read(), + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Internet :: WWW/HTTP :: HTTP Servers', + 'Topic :: Utilities', + ], + keywords='httpd http echo server', + author='Ross Patterson', + author_email='me@rpatterson.net', + url='https://github.com/rpatterson/httpd-echo', + license='GPL', + packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), + include_package_data=True, + py_modules=['httpdecho'], + zip_safe=False, + install_requires=[ + # -*- Extra requirements: -*- + 'six', + ], + tests_require=test_requires, + extras_require=dict(test=test_requires), + test_suite='tests', + entry_points=dict(console_scripts=['httpd-echo=httpdecho:main']), ) diff --git a/tox.ini b/tox.ini index b3880d1..c2ce608 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py3{6,7,8,9,10},pypy{2,3} +envlist = py27,py3{6,7,8,9,10,11},pypy{2,3} [testenv] commands = python setup.py test From 8ec57e8b99107a5b12cb4e2eae100a5b52f4d49f Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Wed, 13 Jul 2022 12:18:33 +0300 Subject: [PATCH 12/20] breaking: python 3.6, setup.cfg, build... --- .gitignore | 3 ++ MANIFEST.in | 2 + README.rst | 7 +++- httpdecho.py | 107 +++++++++++++++++++++---------------------------- pyproject.toml | 8 ++++ pytest.ini | 22 ++++++++++ setup.cfg | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 49 ++-------------------- tests.py | 14 ------- tox.ini | 35 +++++++++++++++- 10 files changed, 227 insertions(+), 125 deletions(-) create mode 100644 MANIFEST.in create mode 100644 pyproject.toml create mode 100644 pytest.ini create mode 100644 setup.cfg delete mode 100644 tests.py diff --git a/.gitignore b/.gitignore index cf39ccc..58cf8f4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,11 @@ /.tox/ /dist/ /httpd_echo.egg-info/ +/httpdecho.egg-info/ /__pycache__/ *.pyc *.pyo /.project /.pydevproject +/.coverage +/.settings diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..8d28fcf --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include tox.ini +include pytest.ini diff --git a/README.rst b/README.rst index 122853b..d5dcc2d 100644 --- a/README.rst +++ b/README.rst @@ -21,16 +21,17 @@ starting at 8000 to try and be as predictable as possible:: >>> import sys >>> import time >>> import subprocess - >>> from six.moves import SimpleHTTPServer >>> startup_delay = 0.5 >>> simple_popen = subprocess.Popen( - ... [sys.executable, '-m', SimpleHTTPServer.__name__] + ... [sys.executable, '-m', 'http.server'] ... ); time.sleep(1) >>> echo_popen = subprocess.Popen( ... [sys.executable, '-m', 'httpdecho'] ... ); time.sleep(1) >>> echo_popen.poll() >>> simple_popen.kill() + >>> simple_popen.communicate() + (None, None) Once running, HTTP requests are echoed in the responses. The default response body format is basically HTTP header format, from @@ -68,6 +69,8 @@ request, the body or the responses body will contain the POST body:: Shutdown the server:: >>> echo_popen.kill() + >>> echo_popen.communicate() + (None, None) ---------------------------- diff --git a/httpdecho.py b/httpdecho.py index b7a14c1..0eec919 100755 --- a/httpdecho.py +++ b/httpdecho.py @@ -1,50 +1,43 @@ -#!/usr/bin/python +#!/usr/bin/python3 # -*- coding: utf-8 -*- -""" -A Simple Python HTTP server that echos the request in the response. -""" +"""A Simple Python HTTP server that echos the request in the response.""" -import socket +import argparse +from email.generator import BytesGenerator import email.message -from six.moves.urllib import parse -try: - from email.generator import BytesGenerator -except ImportError: - # BBB Python 2 compatibility - from email.generator import Generator as BytesGenerator -from six.moves import BaseHTTPServer +from http.server import BaseHTTPRequestHandler, HTTPServer +import socket +from urllib import parse + +__version__ = '0.3.0' -__all__ = ['EchoHTTPRequestHandler'] +__all__ = ["EchoHTTPRequestHandler"] -class EchoHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): - """ - A Simple Python HTTP server that echos the request in the response. - """ +class EchoHTTPRequestHandler(BaseHTTPRequestHandler): + """A Simple Python HTTP server that echos the request in the response.""" def do_GET(self): # noqa:N802, pylint: disable=invalid-name - """ - Echo a request without a body. - """ + """Echo a request without a body.""" message = self.get_message() self.send_head() BytesGenerator(self.wfile).flatten(message, unixfrom=False) do_HEAD = do_GET # noqa:N815 + do_OPTIONS = do_GET # noqa:N815 + do_DELETE = do_GET # noqa:N815 def do_POST(self): # noqa:N802, pylint: disable=invalid-name - """ - Echo a request with a body. - """ + """Echo a request with a body.""" message = self.get_message() try: - length = int(self.headers['Content-Length']) + length = int(self.headers["Content-Length"]) except (TypeError, ValueError) as exc: - message.set_payload("Invalid Content-Length: " + str(exc)) + message.set_payload(f"Invalid Content-Length: {exc}") else: message.set_payload(self.rfile.read(length)) finally: @@ -52,27 +45,23 @@ def do_POST(self): # noqa:N802, pylint: disable=invalid-name BytesGenerator(self.wfile).flatten(message, unixfrom=False) do_PUT = do_POST # noqa:N815 + do_PATCH = do_POST # noqa:N815 def send_head(self): - """ - Send all the basic, required headers. - """ + """Send all the basic, required headers.""" self.send_response(200) - self.send_header("Content-Type", 'text/rfc822-headers; charset=UTF-8') + self.send_header("Content-Type", "text/rfc822-headers; charset=UTF-8") self.send_header("Last-Modified", self.date_time_string()) self.end_headers() def get_message(self): - """ - Assemble the basic message including query parameters. - """ + """Assemble the basic message including query parameters.""" message = email.message.Message() - message['Method'] = self.command - message['Path'] = self.path + message["Method"] = self.command + message["Path"] = self.path - server_url = parse.SplitResult('http', '{0}:{1}'.format( - self.server.server_name, self.server.server_port), '', '', '') + server_url = parse.SplitResult("http", f"{self.server.server_name}:{self.server.server_port}", "", "", "") request_url = parse.urlsplit(server_url.geturl() + self.path) for header, value in parse.parse_qs(request_url.query).items(): message.add_header(header, value[0]) @@ -81,44 +70,38 @@ def get_message(self): def main(args=None, default_port=8000): - """ - Run the echo HTTP server. - """ - import argparse # pylint: disable=import-outside-toplevel - parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.ArgumentDefaultsHelpFormatter) + """Run the echo HTTP server.""" + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( - '--address', '-a', default='localhost', - help='Hostname or IP address to accept requests on.') + "--address", "-a", default="127.0.0.1", + help="Hostname or IP address to accept requests on.") parser.add_argument( - '--port', '-p', help='Port to accept requests on. ' - 'If not specified, use the first available port after 8000.') + "--port", "-p", type=int, + help="Port number to accept requests on. If not specified, use the first available port after 8000.") args = parser.parse_args(args) - port = args.port - if port is None: - port = default_port + if args.port is None: + args.port = default_port bound = False while not bound: try: - httpd = BaseHTTPServer.HTTPServer( - (args.address, port), EchoHTTPRequestHandler) - except socket.error: - port += 1 - if port > 65535: - # Silence pylint to keep it python2 compatible. - raise ValueError('No available port found') # pylint: disable=raise-missing-from + httpd = HTTPServer((args.address, args.port), EchoHTTPRequestHandler) + except socket.error as exc: + args.port += 1 + if args.port > 65535: + raise ValueError("No available port found") from exc else: bound = True else: - httpd = BaseHTTPServer.HTTPServer( - (args.address, int(port)), EchoHTTPRequestHandler) + httpd = HTTPServer((args.address, args.port), EchoHTTPRequestHandler) - print('Echoing HTTP at http://{0}:{1} ...'.format(args.address, port)) - httpd.serve_forever() + print(f"Echoing HTTP at http://{args.address}:{args.port}, press Ctrl+C to stop.") + try: + httpd.serve_forever() + except KeyboardInterrupt: + print(" - Stopped") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..eb84fa2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,8 @@ +[build-system] + +requires = [ + "setuptools>=41", + "wheel" +] + +build-backend = "setuptools.build_meta" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..b0a2e4d --- /dev/null +++ b/pytest.ini @@ -0,0 +1,22 @@ +[pytest] +minversion = 3.4.2 +log_format = %(asctime)s %(levelname)s %(message)s +log_date_format = %Y-%m-%d %H:%M:%S +addopts = + -ra -q + --doctest-modules --doctest-glob='*.rst' + --cov=. --cov-branch + --pdbcls=IPython.terminal.debugger:TerminalPdb +# --pdb + +timeout = 300 +testpaths = + . + +doctest_rst = enabled +doctest_optionflags = + NORMALIZE_WHITESPACE + ELLIPSIS + +filterwarnings = + ignore::UserWarning diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..c6501d0 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,105 @@ +[metadata] +name = httpdecho +version = attr: httpdecho.__version__ +description = A Simple Python HTTP server that echos the request in the response +long_description = file: README.rst +long_description_content_type = text/x-rst +keywords = httpd, http, echo, server +author = Ross Patterson +author_email = me@rpatterson.net +maintainer = Doncho N. Gunchev +maintainer_email = dgunchev@gmail.com +url = https://github.com/mr700/httpd-echo +license = GPL +platforms = any +classifiers = + Development Status :: 4 - Beta + Environment :: Console + Intended Audience :: Developers + License :: OSI Approved :: GNU General Public License (GPL) + Natural Language :: English + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 3.6 + Topic :: Internet :: WWW/HTTP :: HTTP Servers + Topic :: Utilities + + +[options] +zip_safe = False +python_requires = >= 3.6 +py_modules = httpdecho +include_package_data = True + +setup_requires = + setuptools>=41.0 + +tests_require = + requests + coverage + pytest + + +[options.entry_points] +console_scripts = + httpd-echo = httpdecho:main + + +[bdist_wheel] +universal = true + + +[options.package_data] +* = *.rst + + +# python setup.py sdist +[sdist] +# formats = zip,gztar,bztar,xztar +formats = zip,xztar + + +[coverage:run] +branch = true +concurrency = thread + + +[coverage:report] +# Regexes for lines to exclude from consideration +show_missing = true +skip_covered = false +skip_empty = true +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + return NotImplemented + + # Don't complain if non-runnable code isn't run: + if 0: + if False: + if __name__ == .__main__.: + if typing.TYPE_CHECKING: + + # Don't complain about abstract methods, they aren't run: + @(abc\.)?abstractmethod + @(abc\.)?abstractproperty + + # Multiple library version support + except ImportError + + +[coverage:html] +directory = htmlcov + + +[coverage:paths] +source = + ./ diff --git a/setup.py b/setup.py index 52d9783..791dcf2 100755 --- a/setup.py +++ b/setup.py @@ -1,47 +1,6 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- +from setuptools import setup -from setuptools import setup, find_packages -import os -version = '0.2.0' - -test_requires = ['requests'] - -setup( - name='httpd-echo', - version=version, - description=( - "A Simple Python HTTP server that echos the request in the response" - ), - long_description=open( - os.path.join(os.path.dirname(__file__), 'README.rst')).read(), - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Internet :: WWW/HTTP :: HTTP Servers', - 'Topic :: Utilities', - ], - keywords='httpd http echo server', - author='Ross Patterson', - author_email='me@rpatterson.net', - url='https://github.com/rpatterson/httpd-echo', - license='GPL', - packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), - include_package_data=True, - py_modules=['httpdecho'], - zip_safe=False, - install_requires=[ - # -*- Extra requirements: -*- - 'six', - ], - tests_require=test_requires, - extras_require=dict(test=test_requires), - test_suite='tests', - entry_points=dict(console_scripts=['httpd-echo=httpdecho:main']), -) +if __name__ == '__main__': + # https://stackoverflow.com/a/58534041/1136400 + setup() diff --git a/tests.py b/tests.py deleted file mode 100644 index 58f31c3..0000000 --- a/tests.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Test the echo HTTP server. -""" - -import doctest - -from six.moves import SimpleHTTPServer # noqa - - -def load_tests(loader=None, tests=None, ignore=None): - return doctest.DocFileSuite( - 'README.rst', optionflags=doctest.REPORT_NDIFF) diff --git a/tox.ini b/tox.ini index c2ce608..86c8e67 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,36 @@ [tox] -envlist = py27,py3{6,7,8,9,10,11},pypy{2,3} +envlist = py3{6,7,8,9,10,11},pypy3 +# At least this version is needed for PEP 517/518 support. +minversion = 3.3.0 +# Activate isolated build environment. tox will use a virtual environment +# to build a source distribution from the source tree. For build tools and +# arguments use the pyproject.toml file as specified in PEP-517 and PEP-518. +isolated_build = true + + [testenv] -commands = python setup.py test +commands = + check-manifest # confirm items checked into vcs are in your sdist + python setup.py check -m -s # confirm required package meta-data in setup.py + flake8 . + python -m pytest . + +setenv = + PYTHONPATH = {toxinidir} + +deps = + requests + coverage + pytest-cov + pytest + check-manifest >= 0.42 + readme_renderer # confirms your long_description will render correctly on PyPI. + flake8 + + + +[flake8] +select = E,W,F +max-line-length=120 +exclude = .tox, *.egg, build, data, .git, .eggs, __pycache__, test/, docs/, build/, dist/, env.py From 285a94eb05a477f62dcce2e5f1eedeac440a7153 Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Mon, 18 Jul 2022 15:11:48 +0300 Subject: [PATCH 13/20] run pylint in github --- .github/workflows/pylint.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/pylint.yml diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml new file mode 100644 index 0000000..e155d7c --- /dev/null +++ b/.github/workflows/pylint.yml @@ -0,0 +1,23 @@ +name: Pylint + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.6", "3.10"] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + - name: Analysing the code with pylint + run: | + pylint httpdecho.py From 9246932401dabb3c2239f38e769cb44ae8a36389 Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Tue, 19 Jul 2022 13:15:25 +0300 Subject: [PATCH 14/20] feat: Add a Makefile, python-app.yml Signed-off-by: Doncho N. Gunchev --- .github/workflows/pylint.yml | 23 ------------- .github/workflows/python-app.yml | 55 ++++++++++++++++++++++++++++++++ .gitignore | 14 ++++---- MANIFEST.in | 1 + Makefile | 26 +++++++++++++++ setup.cfg | 4 +++ setup.py | 8 +++-- tox.ini | 7 ++-- 8 files changed, 105 insertions(+), 33 deletions(-) delete mode 100644 .github/workflows/pylint.yml create mode 100644 .github/workflows/python-app.yml create mode 100644 Makefile diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml deleted file mode 100644 index e155d7c..0000000 --- a/.github/workflows/pylint.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Pylint - -on: [push] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.6", "3.10"] - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pylint - - name: Analysing the code with pylint - run: | - pylint httpdecho.py diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..0c95572 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,55 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python application + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest pytest-cov pytest-mock requests coverage pylint pycodestyle check-manifest readme_renderer + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: check-manifest + run: | + check-manifest + - name: confirm required package meta-data in setup.py + run: | + python setup.py check -m -s + - name: Analysing the code with pylint + run: | + pylint --max-line-length=127 $(git ls-files '*.py') + - name: Analysing the code with pycodestyle (formerly called pep8) + run: | + pycodestyle --max-line-length=127 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest diff --git a/.gitignore b/.gitignore index 58cf8f4..8e4a4d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,14 @@ +/.coverage /.eggs/ +/.mypy_cache/ +/.project +/.pydevproject +/.settings /.tox/ +/build/ /dist/ -/httpd_echo.egg-info/ -/httpdecho.egg-info/ +/*.egg-info/ /__pycache__/ *.pyc +*.pyd *.pyo -/.project -/.pydevproject -/.coverage -/.settings diff --git a/MANIFEST.in b/MANIFEST.in index 8d28fcf..6bf1cf4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include tox.ini include pytest.ini +include Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ad304e8 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +export PYTHONPATH=$(shell dirname "$(abspath $(lastword $(MAKEFILE_LIST)))") +name:=httpdecho + + +.PHONY: test +test: + python3 -m pytest -v + + +.PHONY: lint +lint: + python3 -m pylint *.py + + +.PHONY: build +build: + python3 -m build + + +.PHONY: clean +clean: + -python3 -m coverage erase + rm -rf site.py build/ dist/ .tox/ .pytest_cache/ .mypy_cache/ + find . -depth \( -name '*.pyc' -o -name '__pycache__' -o -name '__pypackages__' \ + -o -name '*.pyd' -o -name '*.pyo' -o -name '*.egg-info' \ + -o -name '*.py,cover' \) -exec rm -rf \{\} \; diff --git a/setup.cfg b/setup.cfg index c6501d0..7c5c66b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,6 +38,9 @@ tests_require = requests coverage pytest + pytest-cov + pytest-mock + [options.entry_points] @@ -62,6 +65,7 @@ formats = zip,xztar [coverage:run] branch = true concurrency = thread +omit=setup.py [coverage:report] diff --git a/setup.py b/setup.py index 791dcf2..5173c80 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,10 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +"""setup.py for the Simple Python HTTP server that echos the request in the response.""" + from setuptools import setup if __name__ == '__main__': - # https://stackoverflow.com/a/58534041/1136400 - setup() + setup() # https://stackoverflow.com/a/58534041/1136400 diff --git a/tox.ini b/tox.ini index 86c8e67..40a9f18 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,10 @@ isolated_build = true commands = check-manifest # confirm items checked into vcs are in your sdist python setup.py check -m -s # confirm required package meta-data in setup.py - flake8 . + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics python -m pytest . setenv = @@ -32,5 +35,5 @@ deps = [flake8] select = E,W,F -max-line-length=120 +max-line-length=127 exclude = .tox, *.egg, build, data, .git, .eggs, __pycache__, test/, docs/, build/, dist/, env.py From e068ee7b9f9d7a4617b88439ebaa2b2cda10442f Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Thu, 25 Jan 2024 14:45:36 +0200 Subject: [PATCH 15/20] Add PyCharm configuration. --- .idea/.gitignore | 8 +++ .idea/httpd-echo.iml | 14 ++++ .idea/inspectionProfiles/Project_Default.xml | 65 +++++++++++++++++++ .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/misc.xml | 7 ++ .idea/modules.xml | 8 +++ .idea/vcs.xml | 6 ++ 7 files changed, 114 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/httpd-echo.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/httpd-echo.iml b/.idea/httpd-echo.iml new file mode 100644 index 0000000..106eec9 --- /dev/null +++ b/.idea/httpd-echo.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..0a6212d --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,65 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..db8786c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..62eba3f --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From c05dee72a7a3dc18030622a07268b66bc9a9e258 Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Thu, 25 Jan 2024 14:48:25 +0200 Subject: [PATCH 16/20] Cleanup. --- .github/workflows/python-app.yml | 2 +- README.rst | 22 ++++++++++++++++++---- setup.cfg | 16 ++++++++-------- setup.py | 3 +-- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 0c95572..bf96e47 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + python-version: ["3.6", "3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v3 diff --git a/README.rst b/README.rst index d5dcc2d..c547f42 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ -========== httpd-echo ========== + A Simple Python HTTP server that echos the request in the response ------------------------------------------------------------------ @@ -73,13 +73,27 @@ Shutdown the server:: (None, None) ----------------------------- +---- TODO ----------------------------- +---- + + Features for future releases ____________________________ -``Content-Type`` and ``Accept`` support for content negotiation: +- ``Content-Type`` and ``Accept`` support for content negotiation: Return the response body in the format specified in the ``Accept`` header if given, otherwise in the same ``Content-Type`` as the request. + +- ``HTTP 2/0`` and further support. + +Tests +_____ + +- Use pytest and coverage to test every single line. + +Pypi +---- + +- Upload, what else? ;-) diff --git a/setup.cfg b/setup.cfg index 7c5c66b..1aa92d1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,7 +9,7 @@ author = Ross Patterson author_email = me@rpatterson.net maintainer = Doncho N. Gunchev maintainer_email = dgunchev@gmail.com -url = https://github.com/mr700/httpd-echo +url = https://github.com/gunchev/httpd-echo license = GPL platforms = any classifiers = @@ -65,7 +65,7 @@ formats = zip,xztar [coverage:run] branch = true concurrency = thread -omit=setup.py +omit = setup.py [coverage:report] @@ -74,29 +74,29 @@ show_missing = true skip_covered = false skip_empty = true exclude_lines = - # Have to re-enable the standard pragma +# Have to re-enable the standard pragma pragma: no cover - # Don't complain about missing debug-only code: +# Don't complain about missing debug-only code: def __repr__ if self\.debug - # Don't complain if tests don't hit defensive assertion code: +# Don't complain if tests don't hit defensive assertion code: raise AssertionError raise NotImplementedError return NotImplemented - # Don't complain if non-runnable code isn't run: +# Don't complain if non-runnable code isn't run: if 0: if False: if __name__ == .__main__.: if typing.TYPE_CHECKING: - # Don't complain about abstract methods, they aren't run: +# Don't complain about abstract methods, they aren't run: @(abc\.)?abstractmethod @(abc\.)?abstractproperty - # Multiple library version support +# Multiple library version support except ImportError diff --git a/setup.py b/setup.py index 5173c80..fd3bd4b 100755 --- a/setup.py +++ b/setup.py @@ -5,6 +5,5 @@ from setuptools import setup - if __name__ == '__main__': - setup() # https://stackoverflow.com/a/58534041/1136400 + setup() # https://stackoverflow.com/a/58534041/1136400 From 0e5a4f71b51ee0087fc07a46ea0382e30f244626 Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Thu, 25 Jan 2024 14:49:02 +0200 Subject: [PATCH 17/20] Return the cookies sent as headers. --- httpdecho.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/httpdecho.py b/httpdecho.py index 0eec919..aa69e00 100755 --- a/httpdecho.py +++ b/httpdecho.py @@ -4,15 +4,15 @@ """A Simple Python HTTP server that echos the request in the response.""" import argparse -from email.generator import BytesGenerator import email.message -from http.server import BaseHTTPRequestHandler, HTTPServer import socket +from email.generator import BytesGenerator +from http.cookies import SimpleCookie +from http.server import BaseHTTPRequestHandler, HTTPServer from urllib import parse __version__ = '0.3.0' - __all__ = ["EchoHTTPRequestHandler"] @@ -25,11 +25,11 @@ def do_GET(self): # noqa:N802, pylint: disable=invalid-name self.send_head() BytesGenerator(self.wfile).flatten(message, unixfrom=False) - do_HEAD = do_GET # noqa:N815 + do_HEAD = do_GET # noqa:N815 do_OPTIONS = do_GET # noqa:N815 - do_DELETE = do_GET # noqa:N815 + do_DELETE = do_GET # noqa:N815 def do_POST(self): # noqa:N802, pylint: disable=invalid-name """Echo a request with a body.""" @@ -44,14 +44,15 @@ def do_POST(self): # noqa:N802, pylint: disable=invalid-name self.send_head() BytesGenerator(self.wfile).flatten(message, unixfrom=False) - do_PUT = do_POST # noqa:N815 + do_PUT = do_POST # noqa:N815 do_PATCH = do_POST # noqa:N815 def send_head(self): """Send all the basic, required headers.""" self.send_response(200) - self.send_header("Content-Type", "text/rfc822-headers; charset=UTF-8") + # self.send_header("Content-Type", "text/rfc822-headers; charset=UTF-8") + self.send_header("Content-Type", "text/plain; charset=UTF-8") self.send_header("Last-Modified", self.date_time_string()) self.end_headers() @@ -63,8 +64,12 @@ def get_message(self): server_url = parse.SplitResult("http", f"{self.server.server_name}:{self.server.server_port}", "", "", "") request_url = parse.urlsplit(server_url.geturl() + self.path) - for header, value in parse.parse_qs(request_url.query).items(): - message.add_header(header, value[0]) + for name, value in parse.parse_qs(request_url.query).items(): + message.add_header(name, value[0]) + + cookies = SimpleCookie(self.headers.get('Cookie')) + for name, value in cookies.items(): + message.add_header(f'Cookie-{name}', repr(value)) return message From 1a18a7708bfbb3c1debb43045410822e8c49b817 Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Thu, 25 Jan 2024 14:59:42 +0200 Subject: [PATCH 18/20] Version 0.3.1. --- .github/workflows/python-app.yml | 64 ++++++++++++++++---------------- .idea/httpd-echo.iml | 1 + httpdecho.py | 2 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index bf96e47..2fe5015 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -19,37 +19,37 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.6", "3.9", "3.10", "3.11"] + python-version: [ "3.6.15", "3.9.18", "3.10.13", "3.12.1" ] steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install flake8 pytest pytest-cov pytest-mock requests coverage pylint pycodestyle check-manifest readme_renderer - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: check-manifest - run: | - check-manifest - - name: confirm required package meta-data in setup.py - run: | - python setup.py check -m -s - - name: Analysing the code with pylint - run: | - pylint --max-line-length=127 $(git ls-files '*.py') - - name: Analysing the code with pycodestyle (formerly called pep8) - run: | - pycodestyle --max-line-length=127 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with pytest - run: | - pytest + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest pytest-cov pytest-mock requests coverage pylint pycodestyle check-manifest readme_renderer + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: check-manifest + run: | + check-manifest + - name: confirm required package meta-data in setup.py + run: | + python setup.py check -m -s + - name: Analysing the code with pylint + run: | + pylint --max-line-length=127 $(git ls-files '*.py') + - name: Analysing the code with pycodestyle (formerly called pep8) + run: | + pycodestyle --max-line-length=127 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest diff --git a/.idea/httpd-echo.iml b/.idea/httpd-echo.iml index 106eec9..3373c82 100644 --- a/.idea/httpd-echo.iml +++ b/.idea/httpd-echo.iml @@ -2,6 +2,7 @@ + diff --git a/httpdecho.py b/httpdecho.py index aa69e00..817126e 100755 --- a/httpdecho.py +++ b/httpdecho.py @@ -11,7 +11,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer from urllib import parse -__version__ = '0.3.0' +__version__ = '0.3.1' __all__ = ["EchoHTTPRequestHandler"] From 883bc4f754d849e64da92f872616e481850cef92 Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Thu, 25 Jan 2024 15:03:56 +0200 Subject: [PATCH 19/20] Make tox and friends happy. --- .github/workflows/python-app.yml | 2 +- MANIFEST.in | 6 ++++++ tox.ini | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 2fe5015..ad04a3f 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.6.15", "3.9.18", "3.10.13", "3.12.1" ] + python-version: [ "3.9.18", "3.10.13", "3.12.1" ] steps: - uses: actions/checkout@v3 diff --git a/MANIFEST.in b/MANIFEST.in index 6bf1cf4..d6435fc 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,9 @@ include tox.ini include pytest.ini include Makefile +include .idea/httpd-echo.iml +include .idea/inspectionProfiles/Project_Default.xml +include .idea/inspectionProfiles/profiles_settings.xml +include .idea/misc.xml +include .idea/modules.xml +include .idea/vcs.xml diff --git a/tox.ini b/tox.ini index 40a9f18..a437d0e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py3{6,7,8,9,10,11},pypy3 +envlist = py3{6,7,8,9,10,11},pypy3.9 # At least this version is needed for PEP 517/518 support. minversion = 3.3.0 # Activate isolated build environment. tox will use a virtual environment From 9de52e313d48dfcbe942a73ec7db9dbee5f8e303 Mon Sep 17 00:00:00 2001 From: "Doncho N. Gunchev" Date: Thu, 25 Jan 2024 15:59:29 +0200 Subject: [PATCH 20/20] Cosmetics... --- httpdecho.py | 3 ++- tox.ini | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/httpdecho.py b/httpdecho.py index 817126e..94416e6 100755 --- a/httpdecho.py +++ b/httpdecho.py @@ -62,7 +62,8 @@ def get_message(self): message["Method"] = self.command message["Path"] = self.path - server_url = parse.SplitResult("http", f"{self.server.server_name}:{self.server.server_port}", "", "", "") + server_url = parse.SplitResult(scheme="http", netloc=f"{self.server.server_name}:{self.server.server_port}", + path="", query="", fragment="") request_url = parse.urlsplit(server_url.geturl() + self.path) for name, value in parse.parse_qs(request_url.query).items(): message.add_header(name, value[0]) diff --git a/tox.ini b/tox.ini index a437d0e..9ccaf18 100644 --- a/tox.ini +++ b/tox.ini @@ -13,9 +13,9 @@ isolated_build = true commands = check-manifest # confirm items checked into vcs are in your sdist python setup.py check -m -s # confirm required package meta-data in setup.py - # stop the build if there are Python syntax errors or undefined names +# stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide +# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics python -m pytest . @@ -35,5 +35,5 @@ deps = [flake8] select = E,W,F -max-line-length=127 +max-line-length = 127 exclude = .tox, *.egg, build, data, .git, .eggs, __pycache__, test/, docs/, build/, dist/, env.py