From d4318ae700fbd5a94fdb9d28f18b07d7df282ac4 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Fri, 28 Jul 2023 15:36:32 -0400 Subject: [PATCH 01/13] Convert to pyproject.toml --- MANIFEST.in | 3 -- pyproject.toml | 79 ++++++++++++++++++++++++++++++++++++++ requires/development.txt | 3 -- requires/docs.txt | 1 - requires/installation.txt | 1 - requires/testing.txt | 3 -- setup.cfg | 22 ----------- setup.py | 61 ----------------------------- sprockets/http/__init__.py | 12 ++++-- tox.ini | 27 ------------- 10 files changed, 88 insertions(+), 124 deletions(-) delete mode 100644 MANIFEST.in create mode 100644 pyproject.toml delete mode 100644 requires/development.txt delete mode 100644 requires/docs.txt delete mode 100644 requires/installation.txt delete mode 100644 requires/testing.txt delete mode 100644 setup.cfg delete mode 100755 setup.py delete mode 100644 tox.ini diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 1705c55..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include LICENSE -graft docs -graft requires diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a81007f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,79 @@ +[build-system] +requires = ["hatchling>=1.5"] +build-backend = "hatchling.build" + +[project] +name = "sprockets.http" +version = "3.0.0.dev0" +readme = "README.rst" +description = "Tornado HTTP application runner" +author = [{ name = "AWeber Communications, Inc.", email = "api@aweber.com" }] +license = "BSD-3-Clause" +license-files = { paths = ["LICENSE"] } +requires-python = ">=3.7" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: No Input/Output (Daemon)", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Libraries :: Python Modules", +] +dependencies = [ + "importlib-metadata; python_version<'3.9'", + "tornado>=5,<7", +] + +[project.optional-dependencies] +sentry = [ + "sentry-sdk>=1.5.4,<2", +] + +[tool.hatch.build.targets.sdist] +include = [ + "docs", + "examples.py", + "sprockets/**/*.py", + "tests.py", +] + +[tool.hatch.build.targets.wheel] +include = [ + "sprockets/**/*.py", +] + + +[tool.coverage.html] +directory = "build/coverage" + +[tool.coverage.report] +show_missing = true + +[tool.coverage.run] +branch = true +source = ["sprockets"] + +[tool.hatch.envs.default] +dependencies = [ + "coverage>=7.2,<8", + "flake8>=6,<7", + "isort>=5.12,<6", + "pytest>=7.4,<8", + "yapf==0.40.1", +] +features = ["sentry"] + +[tool.isort] +force_grid_wrap = 0 +known_local_folder = ["examples", "sprockets.http"] +multi_line_output = 3 diff --git a/requires/development.txt b/requires/development.txt deleted file mode 100644 index 81342a5..0000000 --- a/requires/development.txt +++ /dev/null @@ -1,3 +0,0 @@ --e '.[sentry]' --r testing.txt --r docs.txt diff --git a/requires/docs.txt b/requires/docs.txt deleted file mode 100644 index 49650fa..0000000 --- a/requires/docs.txt +++ /dev/null @@ -1 +0,0 @@ -sphinx==4.5.0 diff --git a/requires/installation.txt b/requires/installation.txt deleted file mode 100644 index 616640f..0000000 --- a/requires/installation.txt +++ /dev/null @@ -1 +0,0 @@ -tornado>=5,<7 diff --git a/requires/testing.txt b/requires/testing.txt deleted file mode 100644 index 525b366..0000000 --- a/requires/testing.txt +++ /dev/null @@ -1,3 +0,0 @@ -coverage==6.3.1 -flake8==4.0.1 -tox==3.24.5 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 14d2366..0000000 --- a/setup.cfg +++ /dev/null @@ -1,22 +0,0 @@ -[bdist_wheel] -universal = 1 - -[build_sphinx] -all-files = 1 -fresh-env = 1 -warning-is-error = 1 - -[flake8] -exclude = env,build - -[upload_docs] -upload_dir = build/sphinx/html - -[coverage:run] -branch = True -command_line = -m unittest discover tests --buffer --verbose - -[coverage:report] -show_missing = True -include = - sprockets/* diff --git a/setup.py b/setup.py deleted file mode 100755 index 4887be5..0000000 --- a/setup.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -# - -import pathlib - -import setuptools - -from sprockets import http - - -def read_requirements(name): - requirements = [] - for line in pathlib.Path('requires', name).read_text().split('\n'): - if '#' in line: - line = line[:line.index('#')] - line = line.strip() - if line.startswith('-'): - pass - requirements.append(line) - return requirements - - -setuptools.setup( - name='sprockets.http', - version=http.__version__, - description='Tornado HTTP application runner', - author='AWeber Communications', - author_email='api@aweber.com', - url='https://github.com/sprockets/sprockets.http', - install_requires=read_requirements('installation.txt'), - license='BSD', - namespace_packages=['sprockets'], - packages=setuptools.find_packages(), - entry_points={ - 'distutils.commands': ['httprun=sprockets.http.runner:RunCommand'], - }, - extras_require={ - 'sentry': ['sentry-sdk>=1.5.4,<2'], - }, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: No Input/Output (Daemon)', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: Implementation :: CPython', - 'Topic :: Internet :: WWW/HTTP', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Python Modules'], - tests_require=read_requirements('testing.txt'), - python_requires='>=3.5', - zip_safe=True, -) diff --git a/sprockets/http/__init__.py b/sprockets/http/__init__.py index 8dbfed6..32e7b29 100644 --- a/sprockets/http/__init__.py +++ b/sprockets/http/__init__.py @@ -4,6 +4,11 @@ import sys import warnings +try: + from importlib import metadata +except ImportError: # pragma: no cover + import importlib_metadata as metadata + try: import sentry_sdk import sentry_sdk.integrations.logging @@ -17,9 +22,10 @@ except ModuleNotFoundError: pass - -version_info = (2, 5, 0) -__version__ = '.'.join(str(v) for v in version_info) +version = metadata.version('sprockets-http') +version_info: list = [int(c) for c in version.split('.')[:3]] +version_info.extend(version.split('.')[3:]) +__version__ = version _unspecified = object() diff --git a/tox.ini b/tox.ini deleted file mode 100644 index eb3930c..0000000 --- a/tox.ini +++ /dev/null @@ -1,27 +0,0 @@ -[tox] -envlist = py37,py38,py39,tornado,tornado50 -indexserver = - default = https://pypi.python.org/simple -toxworkdir = build/tox -skip_missing_interpreters = True -use_develop = True - -[testenv] -commands = - coverage run - -deps = - . - -rrequires/testing.txt -extras = - sentry - -[testenv:tornado] -commands = - {envbindir}/pip install tornado - {[testenv]commands} - -[testenv:tornado50] -commands = - {envbindir}/pip install tornado==5.0 - {[testenv]commands} From 0963a54d92970a67f702d90e9091f9ed912d6ce3 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Fri, 28 Jul 2023 15:38:17 -0400 Subject: [PATCH 02/13] Run yapf --- docs/conf.py | 4 +- pyproject.toml | 5 +++ sprockets/http/__init__.py | 5 ++- sprockets/http/app.py | 28 +++++++------ sprockets/http/runner.py | 14 +++---- tests.py | 80 +++++++++++++++++++------------------- 6 files changed, 71 insertions(+), 65 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index b68f9cf..c321ecb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,9 +6,7 @@ release = '.'.join(str(v) for v in sprockets.http.version_info[0:2]) extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode' + 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode' ] master_doc = 'index' diff --git a/pyproject.toml b/pyproject.toml index a81007f..fb20f2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,11 @@ dependencies = [ ] features = ["sentry"] +[tool.hatch.envs.default.scripts] +format = [ + "yapf -ir docs sprockets examples.py tests.py", +] + [tool.isort] force_grid_wrap = 0 known_local_folder = ["examples", "sprockets.http"] diff --git a/sprockets/http/__init__.py b/sprockets/http/__init__.py index 32e7b29..3091bf9 100644 --- a/sprockets/http/__init__.py +++ b/sprockets/http/__init__.py @@ -81,8 +81,9 @@ def run(create_application, settings=None, log_config=_unspecified): from . import runner app_settings = {} if settings is None else settings.copy() - debug_mode = bool(app_settings.get('debug', - int(os.environ.get('DEBUG', 0)) != 0)) + debug_mode = bool( + app_settings.get('debug', + int(os.environ.get('DEBUG', 0)) != 0)) app_settings['debug'] = debug_mode if log_config is _unspecified: warnings.warn( diff --git a/sprockets/http/app.py b/sprockets/http/app.py index 28d0bfe..917ab64 100644 --- a/sprockets/http/app.py +++ b/sprockets/http/app.py @@ -105,7 +105,8 @@ def start(self, io_loop): callback(self.tornado_application, io_loop) except Exception: self.logger.error('before_run callback %r cancelled start', - callback, exc_info=1) + callback, + exc_info=1) self.stop(io_loop) raise @@ -142,9 +143,12 @@ def stop(self, io_loop, shutdown_limit=5.0, wait_timeout=1.0): shutdown.add_future(maybe_future) running_async = True except Exception as error: - self.logger.warning('exception raised from shutdown ' - 'callback %r, ignored: %s', - callback, error, exc_info=1) + self.logger.warning( + 'exception raised from shutdown ' + 'callback %r, ignored: %s', + callback, + error, + exc_info=1) if not running_async: shutdown.on_shutdown_ready() @@ -232,6 +236,7 @@ def __init__(self, *args, **kwargs): # use a closure to `self.settings` to allow for dynamic configuration class ServerHeaderTransform(web.OutputTransform): + def transform_first_chunk(_, status_code, headers, chunk, finishing): value = self.settings.get('server_header') @@ -305,9 +310,9 @@ class _ApplicationAdapter(CallbackManager): def __init__(self, application): self._application = application self.settings = self._application.settings - super().__init__( - self._application, - runner_callbacks=getattr(application, 'runner_callbacks', {})) + super().__init__(self._application, + runner_callbacks=getattr(application, + 'runner_callbacks', {})) setattr(self._application, 'runner_callbacks', self.runner_callbacks) @@ -333,11 +338,10 @@ def wrap_application(application, before_run, on_start, shutdown): shutdown = [] if shutdown is None else shutdown if not isinstance(application, Application): - warnings.warn( - 'sprockets.http.run is only going to accept ' - 'sprockets.app.Application instances in 3.0, ' - 'was called with {}'.format(type(application).__name__), - category=DeprecationWarning) + warnings.warn('sprockets.http.run is only going to accept ' + 'sprockets.app.Application instances in 3.0, ' + 'was called with {}'.format(type(application).__name__), + category=DeprecationWarning) application = _ApplicationAdapter(application) application.before_run_callbacks.extend(before_run) diff --git a/sprockets/http/runner.py b/sprockets/http/runner.py index bae939c..efb8d06 100644 --- a/sprockets/http/runner.py +++ b/sprockets/http/runner.py @@ -79,8 +79,8 @@ def start_server(self, port_number, number_of_procs=0): signal.signal(signal.SIGINT, self._on_signal) xheaders = self.application.settings.get('xheaders', True) max_body_size = self.application.settings.get('max_body_size', None) - max_buffer_size = self.application.settings.get('max_buffer_size', - None) + max_buffer_size = self.application.settings.get( + 'max_buffer_size', None) self.server = httpserver.HTTPServer( self.application.tornado_application, @@ -159,8 +159,7 @@ class RunCommand(cmd.Command): description = 'Run a sprockets.http application.' user_options = [ - ('application=', 'a', - 'application callable in `pkg.mod:func` syntax'), + ('application=', 'a', 'application callable in `pkg.mod:func` syntax'), ('env-file=', 'e', 'environment file to import'), ('port=', 'p', 'port for the application to listen on'), ] @@ -175,8 +174,7 @@ def finalize_options(self): raise errors.DistutilsArgError('application is required') if self.env_file and not os.path.exists(self.env_file): raise errors.DistutilsArgError( - 'environment file "{}" does not exist'.format( - self.env_file)) + 'environment file "{}" does not exist'.format(self.env_file)) def run(self): self._read_environment() @@ -204,8 +202,8 @@ def _read_environment(self): name, sep, value = line.strip().partition('=') if sep == '=': - if (value.startswith(('"', "'")) and - value.endswith(value[0])): + if (value.startswith(('"', "'")) + and value.endswith(value[0])): value = value[1:-1] if value: log.info('setting environment %s=%s', name, value) diff --git a/tests.py b/tests.py index 71db30b..de80c7c 100644 --- a/tests.py +++ b/tests.py @@ -32,8 +32,7 @@ def emit(self, record): class RaisingHandler(sprockets.http.mixins.ErrorLogger, - sprockets.http.mixins.ErrorWriter, - web.RequestHandler): + sprockets.http.mixins.ErrorWriter, web.RequestHandler): def get(self, status_code): raise web.HTTPError(int(status_code), @@ -98,18 +97,18 @@ def assert_message_logged(self, level, msg_fmt, *msg_args): for record, message in self.recorder.emitted: if record.levelno == level and message.endswith(suffix): return - self.fail('Expected message ending in "%s" to be logged in %r' - % (suffix, self.recorder.emitted)) + self.fail('Expected message ending in "%s" to be logged in %r' % + (suffix, self.recorder.emitted)) def test_that_client_error_logged_as_warning(self): self.fetch('/status/400') - self.assert_message_logged( - logging.WARNING, 'failed with 400: {}', httputil.responses[400]) + self.assert_message_logged(logging.WARNING, 'failed with 400: {}', + httputil.responses[400]) def test_that_server_error_logged_as_error(self): self.fetch('/status/500') - self.assert_message_logged( - logging.ERROR, 'failed with 500: {}', httputil.responses[500]) + self.assert_message_logged(logging.ERROR, 'failed with 500: {}', + httputil.responses[500]) def test_that_custom_status_codes_logged_as_unknown(self): self.fetch('/status/623') @@ -121,8 +120,8 @@ def test_that_custom_reasons_are_supported(self): def test_that_status_code_extracted_from_http_errors(self): self.fetch('/fail/400') - self.assert_message_logged( - logging.WARNING, 'failed with 400: {}', httputil.responses[400]) + self.assert_message_logged(logging.WARNING, 'failed with 400: {}', + httputil.responses[400]) def test_that_reason_extracted_from_http_errors(self): self.fetch('/fail/400?reason=oopsie') @@ -295,8 +294,7 @@ def test_that_logging_dict_config_is_called_appropriately(self): def test_that_logconfig_override_is_used(self): sprockets.http.run(self.create_app, log_config=mock.sentinel.config) - self.logging_dict_config.assert_called_once_with( - mock.sentinel.config) + self.logging_dict_config.assert_called_once_with(mock.sentinel.config) def test_that_not_specifying_logging_config_is_deprecated(self): with warnings.catch_warnings(record=True) as captured: @@ -396,8 +394,8 @@ def test_that_exceptions_from_shutdown_callbacks_are_ignored(self): def test_that_before_run_callback_invoked(self): runner = sprockets.http.runner.Runner(self.application) runner.run(8080) - self.before_run_callback.assert_called_once_with(self.application, - self.io_loop) + self.before_run_callback.assert_called_once_with( + self.application, self.io_loop) def test_that_exceptions_from_before_run_callbacks_are_terminal(self): another_callback = mock.Mock() @@ -413,8 +411,8 @@ def test_that_exceptions_from_before_run_callbacks_are_terminal(self): runner = sprockets.http.runner.Runner(self.application) runner.run(8080) - self.before_run_callback.assert_called_once_with(self.application, - self.io_loop) + self.before_run_callback.assert_called_once_with( + self.application, self.io_loop) another_callback.assert_not_called() self.shutdown_callback.assert_called_once_with(self.application) sys_exit.assert_called_once_with(70) @@ -502,9 +500,11 @@ def test_that_signal_handler_invokes_shutdown(self): runner._shutdown) def test_that_shutdown_stops_after_timelimit(self): + def add_timeout(_, callback): time.sleep(0.1) callback() + self.io_loop.add_timeout = mock.Mock(side_effect=add_timeout) self.io_loop._timeouts = [mock.Mock()] @@ -557,6 +557,7 @@ def on_started(*args, **kwargs): runner._shutdown() def on_shutdown(*args, **kwargs): + def shutdown_complete(): future.set_result(True) @@ -698,6 +699,7 @@ def patched(): class TestCaseTests(unittest.TestCase): class FakeTest(sprockets.http.testing.SprocketsHttpTestCase): + def get_app(self): self.app = mock.Mock() return self.app @@ -715,18 +717,20 @@ def test_that_teardown_calls_stop(self): test_case.setUp() test_case.io_loop = mock.Mock() test_case.tearDown() - test_case.app.stop.assert_called_once_with( - test_case.io_loop, test_case.shutdown_limit, - test_case.wait_timeout) + test_case.app.stop.assert_called_once_with(test_case.io_loop, + test_case.shutdown_limit, + test_case.wait_timeout) class CorrelationFilterTests(unittest.TestCase): + def setUp(self): super(CorrelationFilterTests, self).setUp() self.logger = logging.getLogger() - self.record = self.logger.makeRecord( - 'name', logging.INFO, 'functionName', 42, 'hello %s', - tuple(['world']), (None, None, None)) + self.record = self.logger.makeRecord('name', logging.INFO, + 'functionName', 42, 'hello %s', + tuple(['world']), + (None, None, None)) self.filter = sprockets.http._CorrelationFilter() def test_that_correlation_filter_adds_correlation_id(self): @@ -741,6 +745,7 @@ def test_that_correlation_filter_does_not_overwrite_correlation_id(self): class LoggingConfigurationTests(unittest.TestCase): + def test_that_debug_sets_log_level_to_debug(self): config = sprockets.http._get_logging_config(True) self.assertEqual(config['root']['level'], 'DEBUG') @@ -759,6 +764,7 @@ def test_that_format_includes_sd_when_service_and_env_are_set(self): class ShutdownHandlerTests(unittest.TestCase): + def setUp(self): super(ShutdownHandlerTests, self).setUp() self.io_loop = ioloop.IOLoop.current() @@ -789,8 +795,8 @@ def test_that_maybe_stop_retries_until_tasks_are_complete(self): fake_loop.time.return_value = 10 wait_timeout = 1.0 - handler = sprockets.http.app._ShutdownHandler( - fake_loop, 5.0, wait_timeout) + handler = sprockets.http.app._ShutdownHandler(fake_loop, 5.0, + wait_timeout) handler._all_tasks = unittest.mock.Mock() handler._all_tasks.return_value = ['does-not-matter'] @@ -799,16 +805,14 @@ def test_that_maybe_stop_retries_until_tasks_are_complete(self): # are outstanding tasks handler.on_shutdown_ready() fake_loop.add_timeout.assert_called_once_with( - fake_loop.time.return_value + wait_timeout, - handler._maybe_stop) + fake_loop.time.return_value + wait_timeout, handler._maybe_stop) fake_loop.add_timeout.reset_mock() # the callback should re-schedule since there are still # outstanding tasks handler._maybe_stop() fake_loop.add_timeout.assert_called_once_with( - fake_loop.time.return_value + wait_timeout, - handler._maybe_stop) + fake_loop.time.return_value + wait_timeout, handler._maybe_stop) fake_loop.add_timeout.reset_mock() # when all of the tasks are finished, the loop is stopped @@ -821,8 +825,8 @@ def test_that_maybe_stop_terminates_when_deadline_reached(self): shutdown_limit = 10 ticks = range(0, shutdown_limit) - handler = sprockets.http.app._ShutdownHandler( - fake_loop, shutdown_limit, 1.0) + handler = sprockets.http.app._ShutdownHandler(fake_loop, + shutdown_limit, 1.0) handler._all_tasks = unittest.mock.Mock() handler._all_tasks.return_value = ['does-not-matter'] @@ -861,10 +865,9 @@ def test_that_log_request_uses_expected_format(self): expected_message = re.compile( r'^%s - - %s "%s %s %s" %d "%s" - "-" "-" \(secs:([^)]*)\)' % (request.remote_ip, - re.escape( - when.strftime('[%d/%b/%Y:%H:%M:%S %z]')), request.method, - re.escape(request.uri), request.version, handler.get_status(), - handler._reason)) + re.escape(when.strftime('[%d/%b/%Y:%H:%M:%S %z]')), + request.method, re.escape(request.uri), request.version, + handler.get_status(), handler._reason)) message = context.records[0].getMessage() match = expected_message.match(message) if match is None: @@ -923,8 +926,7 @@ def test_that_log_request_uses_correct_log_level_with_only_failures(self): class ServerHeaderTests(sprockets.http.testing.SprocketsHttpTestCase): def get_app(self): - self.app = sprockets.http.app.Application( - server_header='a/b/c') + self.app = sprockets.http.app.Application(server_header='a/b/c') return self.app def test_reads_from_settings(self): @@ -952,10 +954,8 @@ def test_defaults(self): version='myversion') self.assertEqual('myservice/myversion', app.settings['server_header']) - app = sprockets.http.app.Application(service='myservice', - version=None) + app = sprockets.http.app.Application(service='myservice', version=None) self.assertEqual('myservice', app.settings['server_header']) - app = sprockets.http.app.Application(service=None, - version='myversion') + app = sprockets.http.app.Application(service=None, version='myversion') self.assertIsNone(app.settings['server_header']) From b64f8055d6585413172899af0e4af7057502b88b Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Fri, 28 Jul 2023 15:39:08 -0400 Subject: [PATCH 03/13] Cleanup imports to match PyCharm --- examples.py | 2 +- pyproject.toml | 1 + sprockets/http/mixins.py | 2 +- sprockets/http/runner.py | 2 +- tests.py | 4 ++-- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples.py b/examples.py index 08e5d02..7b00453 100644 --- a/examples.py +++ b/examples.py @@ -1,7 +1,7 @@ from tornado import web -from sprockets.http import app, mixins import sprockets.http +from sprockets.http import app, mixins class StatusHandler(mixins.ErrorLogger, mixins.ErrorWriter, diff --git a/pyproject.toml b/pyproject.toml index fb20f2d..4c47211 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ features = ["sentry"] [tool.hatch.envs.default.scripts] format = [ + "isort docs sprockets examples.py tests.py", "yapf -ir docs sprockets examples.py tests.py", ] diff --git a/sprockets/http/mixins.py b/sprockets/http/mixins.py index f916f75..b5d8d12 100644 --- a/sprockets/http/mixins.py +++ b/sprockets/http/mixins.py @@ -6,8 +6,8 @@ - :class:`ErrorWriter`: implements ``send_error`` to write a useful response """ -import logging import json +import logging import traceback from tornado import httputil diff --git a/sprockets/http/runner.py b/sprockets/http/runner.py index efb8d06..503197e 100644 --- a/sprockets/http/runner.py +++ b/sprockets/http/runner.py @@ -5,11 +5,11 @@ - :class:`.RunCommand`: distutils command to runs an application """ -from distutils import cmd, errors, log import logging import os.path import signal import sys +from distutils import cmd, errors, log from tornado import httpserver, ioloop diff --git a/tests.py b/tests.py index de80c7c..abed49b 100644 --- a/tests.py +++ b/tests.py @@ -1,4 +1,3 @@ -from unittest import mock import contextlib import datetime import distutils.dist @@ -11,14 +10,15 @@ import unittest import uuid import warnings +from unittest import mock from tornado import concurrent, httpserver, httputil, ioloop, log, testing, web +import examples import sprockets.http.app import sprockets.http.mixins import sprockets.http.runner import sprockets.http.testing -import examples class RecordingHandler(logging.Handler): From c60da5f5052fb56255db6103382fb1aeaf4a3ea4 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Fri, 28 Jul 2023 15:48:44 -0400 Subject: [PATCH 04/13] Add .pre-commit-config --- .gitignore | 2 ++ .pre-commit-config.yaml | 23 +++++++++++++++++++++++ pyproject.toml | 2 ++ 3 files changed, 27 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.gitignore b/.gitignore index 6b60207..98e30f8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ env *.egg-info .coverage /coverage.xml +!.pre-commit-config.yaml +!.github diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4b74d38 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: check-toml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + - id: trailing-whitespace + - repo: https://github.com/google/yapf + rev: v0.40.1 + hooks: + - id: yapf + - repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + additional_dependencies: + - flake8-isort + - flake8-pyproject diff --git a/pyproject.toml b/pyproject.toml index 4c47211..ac58da1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ sentry = [ include = [ "docs", "examples.py", + ".pre-commit-config.yaml", "sprockets/**/*.py", "tests.py", ] @@ -78,6 +79,7 @@ format = [ "isort docs sprockets examples.py tests.py", "yapf -ir docs sprockets examples.py tests.py", ] +lint = "pre-commit run --all-files" [tool.isort] force_grid_wrap = 0 From c7bbe26e3e1e3fa2683075fe69cf5d405c3db952 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Fri, 28 Jul 2023 16:02:57 -0400 Subject: [PATCH 05/13] Fix a loop not running test breakage This was probably introduced when I switched from 3.7 to 3.9. --- tests.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests.py b/tests.py index abed49b..b7cb945 100644 --- a/tests.py +++ b/tests.py @@ -532,7 +532,7 @@ def test_that_calling_with_non_sprockets_application_is_deprecated(self): self.assertEqual(len(captured), 0) -class AsyncRunTests(unittest.TestCase): +class AsyncRunTests(unittest.IsolatedAsyncioTestCase): def test_that_on_start_callbacks_are_invoked(self): future = concurrent.Future() @@ -546,6 +546,8 @@ def on_started(*args, **kwargs): with mock.patch('sprockets.http.runner.Runner.start_server'): runner = sprockets.http.runner.Runner(application, on_start=[on_started]) + runner.wait_timeout = 0.1 + runner.shutdown_limit = 0.25 runner.run(8000) self.assertTrue(future.result()) @@ -569,6 +571,8 @@ def shutdown_complete(): runner = sprockets.http.runner.Runner(application, on_start=[on_started], shutdown=[on_shutdown]) + runner.wait_timeout = 0.1 + runner.shutdown_limit = 0.25 runner.run(8000) self.assertTrue(future.result()) From 49b4aed073c4c750ec9f9b1629ac1bcf028f5fbf Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Fri, 28 Jul 2023 16:03:35 -0400 Subject: [PATCH 06/13] Add "test" script --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index ac58da1..a23d0c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ format = [ "yapf -ir docs sprockets examples.py tests.py", ] lint = "pre-commit run --all-files" +test = "pytest tests.py" [tool.isort] force_grid_wrap = 0 From a6f7288d737885a5af53913e857290489fa94c64 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Fri, 28 Jul 2023 16:04:53 -0400 Subject: [PATCH 07/13] Bump license date --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0f39f88..e9c9204 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2020 AWeber Communications +Copyright (c) 2015-2023 AWeber Communications All rights reserved. Redistribution and use in source and binary forms, with or without modification, From cc03327c86f910ecf580006b0002ecfc8c9b5483 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Fri, 28 Jul 2023 16:17:36 -0400 Subject: [PATCH 08/13] Update sphinx docs --- docs/_static/custom.css | 7 ------- docs/conf.py | 23 ++++++++++------------- docs/custom.css | 1 + pyproject.toml | 3 +++ 4 files changed, 14 insertions(+), 20 deletions(-) delete mode 100644 docs/_static/custom.css create mode 100644 docs/custom.css diff --git a/docs/_static/custom.css b/docs/_static/custom.css deleted file mode 100644 index d0f0c6e..0000000 --- a/docs/_static/custom.css +++ /dev/null @@ -1,7 +0,0 @@ -div.body { max-width: 85% } -div.document { width: 90% } -h1.logo {font-size:inherit} -th.field-name {hyphens: manual; -webkit-hyphens: none} -dl.class>dt {text-indent: -45px; padding-left: 45px} -dl.function dt {text-indent: -45px; padding-left: 45px} -div.seealso {background-color: initial; border: initial} diff --git a/docs/conf.py b/docs/conf.py index c321ecb..36fadd0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,20 +5,17 @@ version = sprockets.http.__version__ release = '.'.join(str(v) for v in sprockets.http.version_info[0:2]) -extensions = [ - 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode' -] +extensions = [] +html_theme = 'python_docs_theme' +html_static_path = ['.'] +html_css_files = ['custom.css'] -master_doc = 'index' -html_theme_options = { - 'github_user': 'sprockets', - 'github_repo': 'sprockets.http', - 'description': 'Tornado application runner', - 'github_banner': True, -} -html_static_path = ['_static'] +# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html +extensions.append('sphinx.ext.autodoc') +# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html +extensions.append('sphinx.ext.intersphinx') intersphinx_mapping = { - 'python': ('http://docs.python.org/3/', None), - 'tornado': ('http://tornadoweb.org/en/latest/', None), + 'python': ('https://docs.python.org/3/', None), + 'tornado': ('https://www.tornadoweb.org/en/latest/', None), } diff --git a/docs/custom.css b/docs/custom.css new file mode 100644 index 0000000..931b56e --- /dev/null +++ b/docs/custom.css @@ -0,0 +1 @@ +div.sphinxsidebarwrapper { overflow-x: scroll } diff --git a/pyproject.toml b/pyproject.toml index a23d0c9..b0c654a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,8 @@ dependencies = [ "flake8>=6,<7", "isort>=5.12,<6", "pytest>=7.4,<8", + "python-docs-theme==2023.5", + "sphinx>=7,<8", "yapf==0.40.1", ] features = ["sentry"] @@ -79,6 +81,7 @@ format = [ "isort docs sprockets examples.py tests.py", "yapf -ir docs sprockets examples.py tests.py", ] +docs = "sphinx-build docs build/sphinx" lint = "pre-commit run --all-files" test = "pytest tests.py" From 531b6e5bb771e912b5dfd385cbac390c82621181 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Fri, 28 Jul 2023 16:29:09 -0400 Subject: [PATCH 09/13] Add readthedocs.yaml --- .readthedocs.yaml | 15 +++++++++++++++ docs/requirements.txt | 2 ++ pyproject.toml | 1 + 3 files changed, 18 insertions(+) create mode 100644 .readthedocs.yaml create mode 100644 docs/requirements.txt diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..a9437a2 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,15 @@ +version: 2 + +build: + os: "ubuntu-22.04" + tools: + python: "3.11" + +sphinx: + builder: html + configuration: docs/conf.py + fail_on_warning: true + +python: + install: + - requirements: docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..c6a4aa1 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +python-docs-theme==2023.5 +sphinx>=7,<8 diff --git a/pyproject.toml b/pyproject.toml index b0c654a..3592af0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ include = [ "docs", "examples.py", ".pre-commit-config.yaml", + ".readthedocs.yaml", "sprockets/**/*.py", "tests.py", ] From 705f65e5280825e26ee400e7659779f24f955827 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Sat, 29 Jul 2023 08:12:16 -0400 Subject: [PATCH 10/13] Update github actions --- .github/workflows/deploy.yaml | 2 +- .github/workflows/testing.yaml | 30 ++++++++++++++++++------------ pyproject.toml | 2 ++ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index dcb1948..1a361d0 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -10,7 +10,7 @@ jobs: container: python:3.10-alpine steps: - name: Checkout repository - uses: actions/checkout@v1 + uses: actions/checkout@v3 - name: Build package run: python3 setup.py bdist_wheel sdist - name: Publish package diff --git a/.github/workflows/testing.yaml b/.github/workflows/testing.yaml index 25adc78..544a25b 100644 --- a/.github/workflows/testing.yaml +++ b/.github/workflows/testing.yaml @@ -4,9 +4,9 @@ on: branches: ["*"] paths-ignore: - 'docs/**' - - 'setup.*' - - '*.md' - '*.rst' + - '.readthedocs.yaml' + - 'LICENSE' tags-ignore: ["*"] pull_request: jobs: @@ -15,30 +15,36 @@ jobs: timeout-minutes: 3 strategy: matrix: - python: ["3.7", "3.8", "3.9", "3.10"] + python: ["3.9", "3.10", "3.11"] container: image: python:${{ matrix.python }}-alpine steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - - name: Install testing dependencies - run: pip3 --no-cache-dir install -e '.[sentry]' -r requires/testing.txt + - name: Create environment + run: | + pip install hatch + hatch dep show requirements --all >requirements.txt + pip install -r requirements.txt + pip install . - - name: Run flake8 tests - run: flake8 + - name: Lint + run: | + flake8 docs sprockets examples.py tests.py + yapf -dr docs sprockets examples.py tests.py - - name: Run tests - run: coverage run + - name: Test + run: coverage run -m pytest tests.py - name: Output coverage run: coverage report && coverage xml - name: Upload Coverage - uses: codecov/codecov-action@v1.0.2 + uses: codecov/codecov-action@v3 if: github.event_name == 'push' && github.repository == 'sprockets/sprockets.http' with: token: ${{secrets.CODECOV_TOKEN}} - file: build/coverage.xml + files: coverage.xml flags: unittests fail_ci_if_error: true diff --git a/pyproject.toml b/pyproject.toml index 3592af0..78c41a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,8 @@ source = ["sprockets"] dependencies = [ "coverage>=7.2,<8", "flake8>=6,<7", + "flake8-isort>=6.0,<7", + "flake8-pyproject>=1.2,<2", "isort>=5.12,<6", "pytest>=7.4,<8", "python-docs-theme==2023.5", From 0df64ad6205d83a3686cf8febb8335d19b021ad1 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Sat, 29 Jul 2023 09:52:34 -0400 Subject: [PATCH 11/13] Update contributing --- docs/contributing.rst | 61 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index d901874..523040e 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -4,22 +4,61 @@ Do you want to contribute fixes or improvements? **AWesome!** *Thank you very much, and let's get started.* -Quickstart Development Guide ----------------------------- +This project uses hatch_ for project automation and pre-commit_ to ensure that your +changes aren't going to be rejected for formatting or style reasons. However, you do +need to install both utilities. You should install it using a specific python version +as a "user" installation.:: -Setup -+++++ + python3.10 -m pip install --user hatch pre-commit -code:: +This will install the utility into ``~/.local/bin`` which you may need to add to +your ``PATH`` environment variable. - python3.10 -m venv env - pip install -r requires/development.txt +.. note:: + You are not required to use ``hatch`` or ``pre-commit`` to contribute to this + project. Using them will make your life a little easier though. The following + sections contain instructions for using a completely vanilla Python virtual + environment which can be adapted to your workflow. Testing -+++++++ +------- +The simplest way to run the tests with coverage is from within a hatch-spawned shell:: -code:: + $ hatch shell + (sprockets-http)$ coverage run -m pytest tests.py + (sprockets-http)$ coverage report - coverage run - coverage report +You can also run the same commands without using an interactive hatch spawned shell:: + + $ hatch run coverage run -m pytest tests.py + $ hatch run coverage report + +Finally, you can simply extract the development requirements using hatch and use a +vanilla virtual environment:: + + $ python3.11 -m venv --upgrade-deps env + $ . ./env/bin/activate + (env) $ pip install hatch + (env) $ hatch dep show requirements --all >requirements.txt + (env) $ pip install -r requirements.txt + (env) $ coverage run -m pytest tests.py + (env) $ coverage report + +Linting +------- +Lint checking is off-loaded to the pre-commit_ utility. After installing the hooks, +any commit is blocked by style checks. You can also run the hooks manually using the +pre-commit_ utility. +:: + + $ pre-commit install --install-hooks + $ pre-commit run --all-files + +Of course, you can run ``flake8`` and ``yapf`` manually inside of a vanilla environment:: + + (env) $ flake8 docs sprockets examples.py tests.py + (env) $ yapf -dr docs sprockets examples.py tests.py + +.. _hatch: https://hatch.pypa.io/latest/ +.. _pre-commit: https://pre-commit.com/ From cd3efe9760c6080ef2a4831fa99e79a2f6917795 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Sun, 30 Jul 2023 09:40:27 -0400 Subject: [PATCH 12/13] Replace travis build badge with github --- README.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 9f19315..1c090d0 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ sprockets.http ============== -|Version| |ReadTheDocs| |Travis| |Coverage| +|Version| |ReadTheDocs| |GitHub| |Coverage| The goal of this library is to make it a little easier to develop great HTTP API services using the Tornado web framework. It concentrates on @@ -256,7 +256,8 @@ for choosing the appropriate response format and sending the error response. :target: https://codecov.io/github/sprockets/sprockets.http .. |ReadTheDocs| image:: http://readthedocs.org/projects/sprocketshttp/badge/?version=master :target: https://sprocketshttp.readthedocs.io/ -.. |Travis| image:: https://travis-ci.org/sprockets/sprockets.http.svg - :target: https://travis-ci.org/sprockets/sprockets.http +.. |GitHub| image:: https://img.shields.io/github/actions/workflow/status/sprockets/sprockets.http/testing.yaml + :alt: GitHub Workflow Status (with event) + :target: https://github.com/sprockets/sprockets.http/actions/workflows/testing.yaml .. |Version| image:: https://badge.fury.io/py/sprockets.http.svg :target: https://pypi.python.org/pypi/sprockets.http/ From 67e3ed3e411088a82685143f5e450bfc1ff08327 Mon Sep 17 00:00:00 2001 From: Dave Shawley Date: Sun, 30 Jul 2023 10:07:51 -0400 Subject: [PATCH 13/13] Rewrite history file --- docs/conf.py | 8 +++ docs/history.rst | 154 ++++++++++++++++++++--------------------------- 2 files changed, 73 insertions(+), 89 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 36fadd0..efee7bc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,3 +19,11 @@ 'python': ('https://docs.python.org/3/', None), 'tornado': ('https://www.tornadoweb.org/en/latest/', None), } + +# https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html +extensions.append('sphinx.ext.extlinks') +extlinks = { + 'compare': + ("https://github.com/sprockets/sprockets.http/compare/%s", "%s"), + 'issue': ("https://github.com/sprockets/sprockets.http/issues/%s", "#%s"), +} diff --git a/docs/history.rst b/docs/history.rst index 06c1408..45b4ead 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -3,50 +3,56 @@ Release History =============== -`2.5.0`_ (26 May 2022) ----------------------- +:compare:`Next <2.5.0...master>` +-------------------------------- +- Replace setuptools with hatch_ + +.. _hatch: https://hatch.pypa.io/latest/ + +:compare:`2.5.0 <2.4.0...2.5.0>` (26 May 2022) +---------------------------------------------- - Add customization of **Server** header -`2.4.0`_ (16 Mar 2022) ----------------------- +:compare:`2.4.0 <2.3.0...2.4.0>` (16 Mar 2022) +---------------------------------------------- - Add support for Python 3.10 - Change the default access log format -`2.3.0`_ (03 Feb 2022) ----------------------- +:compare:`2.3.0 <2.2.0...2.3.0>` (03 Feb 2022) +---------------------------------------------- - Added optional Sentry integration -`2.2.0`_ (28 Sep 2020) ----------------------- +:compare:`2.2.0 <2.1.2...2.2.0>` (28 Sep 2020) +---------------------------------------------- - Change xheaders option to default to ``True`` -`2.1.2`_ (15 Sep 2020) ----------------------- +:compare:`2.1.2 <2.1.1...2.1.2>` (15 Sep 2020) +---------------------------------------------- - Updated to support Python 3.9. ``asyncio.Task.all_tasks`` was removed so I switched to ``asyncio.all_tasks`` if it exists. - Deprecate calling ``sprockets.http.run`` with anything that isn't a ``sprockets.app.Application`` instance. -`2.1.1`_ (19 Feb 2020) ----------------------- +:compare:`2.1.1 <2.1.0...2.1.1>` (19 Feb 2020) +---------------------------------------------- - :meth:`sprockets.http.app.CallbackManager.stop` no longer requires the event loop to be running (fixes `#34`_) .. _#34: https://github.com/sprockets/sprockets.http/issues/34 -`2.1.0`_ (9 Oct 2019) ---------------------- +:compare:`2.1.0 <2.0.1...2.1.0>` (9 Oct 2019) +--------------------------------------------- - Make shutdown timings configurable. - Add :class:`sprockets.http.testing.SprocketsHttpTestCase`. - Deprecate calling :func:`sprockets.http.run` without a specified logging configuration. -`2.0.1`_ (5 Mar 2019) ----------------------- +:compare:`2.0.1 <2.0.0...2.0.1>` (5 Mar 2019) +---------------------------------------------- - Include Tornado 6 in pin -`2.0.0`_ (27 Nov 2018) ----------------------- +:compare:`2.0.0 <1.5.0...2.0.0>` (27 Nov 2018) +---------------------------------------------- - Add support for Tornado 5.0 - Drop support for Tornado versions earlier than 5.0 - Drop support for Python versions earlier than 3.5 @@ -56,128 +62,98 @@ Release History .. _Thread Safety: https://docs.python.org/3/library/logging.html#thread-safety -`1.5.0`_ (29 Jan 2018) ----------------------- +:compare:`1.5.0 <1.4.2...1.5.0>` (29 Jan 2018) +---------------------------------------------- - Enable port reuse for Tornado versions newer than 4.3. -`1.4.2`_ (25 Jan 2018) ----------------------- +:compare:`1.4.2 <1.4.1...1.4.2>` (25 Jan 2018) +---------------------------------------------- - Allow max_body_size and max_buffer_size to be specified on the http server. -`1.4.1`_ (3 Jan 2018) ---------------------- +:compare:`1.4.1 <1.4.0...1.4.1>` (3 Jan 2018) +--------------------------------------------- - Workaround https://bitbucket.org/birkenfeld/sphinx-contrib/issues/184/ by pinning sphinx in the development environment. -`1.4.0`_ (29 Sep 2017) ----------------------- +:compare:`1.4.0 <1.3.3...1.4.0>` (29 Sep 2017) +---------------------------------------------- - Separate the concerns of running the application from the callback chains. The latter has been refactored into :mod:`sprockets.http.app`. This change is completely invisible to the outside world. - Officially deprecated the ``runner_callbacks`` application attribute. -`1.3.3`_ (20 Sept 2016) ------------------------ +:compare:`1.3.3 <1.3.2...1.3.3>` (20 Sept 2016) +----------------------------------------------- - Include correlation-id in the structured log data when logging. -`1.3.2`_ (19 Sept 2016) ------------------------ +:compare:`1.3.2 <1.3.1...1.3.2>` (19 Sept 2016) +----------------------------------------------- - Include the service and environment (if set) in the structured log data. -`1.3.1`_ (16 Sept 2016) ------------------------ +:compare:`1.3.1 <1.3.0...1.3.1>` (16 Sept 2016) +----------------------------------------------- - Change the non-DEBUG log format to include structured data and a leading first byte for log level. -`1.3.0`_ (11 Mar 2016) ----------------------- +:compare:`1.3.0 <1.2.0...1.3.0>` (11 Mar 2016) +---------------------------------------------- - Add ``httprun`` setup.py command. - Use ``declare_namespace`` to declare the sprockets namespace package. - Remove ``JSONRequestFormatter`` logging when not in debug mode - Remove sprockets.logging dependency -`1.2.0`_ (11 Mar 2016) ----------------------- +:compare:`1.2.0 <1.1.2...1.2.0>` (11 Mar 2016) +---------------------------------------------- - Add support for the ``on_start`` callback. - Add support to wait for the completion of ``shutdown`` callbacks that return a future. - Adds new init params to runner.Runner for the three callback types -`1.1.2`_ (23 Feb 2016) ----------------------- +:compare:`1.1.2 <1.1.1...1.1.2>` (23 Feb 2016) +---------------------------------------------- - Allow xheaders to be set in the application.settings. -`1.1.1`_ (15 Feb 2016) ----------------------- +:compare:`1.1.1 <1.1.0...1.1.1>` (15 Feb 2016) +---------------------------------------------- - Delay grabbing the ``IOLoop`` instance until after fork. -`1.1.0`_ (11 Feb 2016) ----------------------- +:compare:`1.1.0 <1.0.2...1.1.0>` (11 Feb 2016) +---------------------------------------------- - Add support for the ``before_run`` callback set. -`1.0.2`_ (10 Dec 2015) ----------------------- +:compare:`1.0.2 <1.0.1...1.0.2>` (10 Dec 2015) +---------------------------------------------- - Add ``log_config`` parameter to ``sprockets.http.run`` -`1.0.1`_ (20 Nov 2015) ----------------------- +:compare:`1.0.1 <1.0.0...1.0.1>` (20 Nov 2015) +---------------------------------------------- - Add support for ``sprockets.mixins.mediatype`` in ``sprockets.http.mixins.ErrorWriter`` -`1.0.0`_ (20 Nov 2015) ----------------------- +:compare:`1.0.0 <0.4.0...1.0.0>` (20 Nov 2015) +---------------------------------------------- - Add ``sprockets.http.mixins.LoggingHandler`` - Add ``sprockets.http.mixins.ErrorLogger`` - Add ``sprockets.http.mixins.ErrorWriter`` -`0.4.0`_ (24 Sep 2015) ----------------------- +:compare:`0.4.0 <0.3.0...0.4.0>` (24 Sep 2015) +---------------------------------------------- - Run callbacks from ``application.runner_callbacks['shutdown']`` when the application is shutting down. - Add ``number_of_procs`` parameter to ``sprockets.http``. -`0.3.0`_ (28 Aug 2015) ----------------------- +:compare:`0.3.0 <0.2.2...0.3.0>` (28 Aug 2015) +---------------------------------------------- - Install :func:`sprockets.logging.tornado_log_function` as the logging function when we are running in release mode -`0.2.2`_ (23 Jul 2015) ----------------------- +:compare:`0.2.2 <0.2.1...0.2.2>` (23 Jul 2015) +---------------------------------------------- + - Fixed requirements management... why is packaging so hard?! -`0.2.1`_ (23 Jul 2015) ----------------------- +:compare:`0.2.1 <0.2.0...0.2.1>` (23 Jul 2015) +---------------------------------------------- - Corrected packaging metadata -`0.2.0`_ (22 Jul 2015) ----------------------- +:compare:`0.2.0 <0.0.0...0.2.0>` (22 Jul 2015) +---------------------------------------------- - Add :func:`sprockets.http.run` - -.. _0.2.0: https://github.com/sprockets/sprockets.http/compare/0.0.0...0.2.0 -.. _0.2.1: https://github.com/sprockets/sprockets.http/compare/0.2.0...0.2.1 -.. _0.2.2: https://github.com/sprockets/sprockets.http/compare/0.2.1...0.2.2 -.. _0.3.0: https://github.com/sprockets/sprockets.http/compare/0.2.2...0.3.0 -.. _0.4.0: https://github.com/sprockets/sprockets.http/compare/0.3.0...0.4.0 -.. _1.0.0: https://github.com/sprockets/sprockets.http/compare/0.4.0...1.0.0 -.. _1.0.1: https://github.com/sprockets/sprockets.http/compare/1.0.0...1.0.1 -.. _1.0.2: https://github.com/sprockets/sprockets.http/compare/1.0.1...1.0.2 -.. _1.1.0: https://github.com/sprockets/sprockets.http/compare/1.0.2...1.1.0 -.. _1.1.1: https://github.com/sprockets/sprockets.http/compare/1.1.0...1.1.1 -.. _1.1.2: https://github.com/sprockets/sprockets.http/compare/1.1.1...1.1.2 -.. _1.2.0: https://github.com/sprockets/sprockets.http/compare/1.0.2...1.2.0 -.. _1.3.0: https://github.com/sprockets/sprockets.http/compare/1.2.0...1.3.0 -.. _1.3.1: https://github.com/sprockets/sprockets.http/compare/1.3.0...1.3.1 -.. _1.3.2: https://github.com/sprockets/sprockets.http/compare/1.3.1...1.3.2 -.. _1.3.3: https://github.com/sprockets/sprockets.http/compare/1.3.2...1.3.3 -.. _1.4.0: https://github.com/sprockets/sprockets.http/compare/1.3.3...1.4.0 -.. _1.4.1: https://github.com/sprockets/sprockets.http/compare/1.4.0...1.4.1 -.. _1.4.2: https://github.com/sprockets/sprockets.http/compare/1.4.1...1.4.2 -.. _1.5.0: https://github.com/sprockets/sprockets.http/compare/1.4.2...1.5.0 -.. _2.0.0: https://github.com/sprockets/sprockets.http/compare/1.5.0...2.0.0 -.. _2.0.1: https://github.com/sprockets/sprockets.http/compare/2.0.0...2.0.1 -.. _2.1.0: https://github.com/sprockets/sprockets.http/compare/2.0.1...2.1.0 -.. _2.1.1: https://github.com/sprockets/sprockets.http/compare/2.1.0...2.1.1 -.. _2.1.2: https://github.com/sprockets/sprockets.http/compare/2.1.1...2.1.2 -.. _2.2.0: https://github.com/sprockets/sprockets.http/compare/2.1.2...2.2.0 -.. _2.3.0: https://github.com/sprockets/sprockets.http/compare/2.2.0...2.3.0 -.. _2.4.0: https://github.com/sprockets/sprockets.http/compare/2.3.0...2.4.0 -.. _2.5.0: https://github.com/sprockets/sprockets.http/compare/2.4.0...2.5.0 -.. _Next Release: https://github.com/sprockets/sprockets.http/compare/2.5.0...master