Repository: sanic-org/sanic

Issue:
 Blueprint with multiple workers not usable on Windows due to pickling error
**Describe the bug**
When using a blueprint with multiple workers on windows, sanic fails on startup due to a failure to pickle the route:

> Exception has occurred: _pickle.PicklingError
Can't pickle <class 'sanic.blueprints.Route'>: attribute lookup Route on sanic.blueprints failed


**Code snippet**
```python
from sanic import Sanic
from sanic import Blueprint
from sanic.response import json

blueprint = Blueprint("API_blueprint")

@blueprint.route("/")
async def test(request):
    return json({"hello": "world"})

app = Sanic()

app.blueprint(blueprint)

def main():
    app.run(workers=2)
```

**Expected behavior**
It should be possible to run with multiple workers using blueprints in the same way as it is using the sanic app object directly

**Environment (please complete the following information):**
 - OS: Windows
 - Version 7


**Additional context**
A similar issue with pickling was [recently fixed](https://github.com/ashleysommer/sanic-cors/issues/14#issuecomment-434968189) in sanic-cors. No idea if the resolution to that one will be helpful



Discussion:
I can look into this.
PR created: https://github.com/huge-success/sanic/pull/1393
The blueprint pickling problem is fixed, though I don't have a windows machine to test that it fixes the multiprocessing issue on Windows.

Resolving patch:
diff --git a/sanic/blueprints.py b/sanic/blueprints.py
--- a/sanic/blueprints.py
+++ b/sanic/blueprints.py
@@ -5,7 +5,7 @@
 
 
 FutureRoute = namedtuple(
-    "Route",
+    "FutureRoute",
     [
         "handler",
         "uri",
@@ -17,11 +17,15 @@
         "name",
     ],
 )
-FutureListener = namedtuple("Listener", ["handler", "uri", "methods", "host"])
-FutureMiddleware = namedtuple("Route", ["middleware", "args", "kwargs"])
-FutureException = namedtuple("Route", ["handler", "args", "kwargs"])
+FutureListener = namedtuple(
+    "FutureListener", ["handler", "uri", "methods", "host"]
+)
+FutureMiddleware = namedtuple(
+    "FutureMiddleware", ["middleware", "args", "kwargs"]
+)
+FutureException = namedtuple("FutureException", ["handler", "args", "kwargs"])
 FutureStatic = namedtuple(
-    "Route", ["uri", "file_or_directory", "args", "kwargs"]
+    "FutureStatic", ["uri", "file_or_directory", "args", "kwargs"]
 )
 
 


Test patch:
diff --git a/tests/test_multiprocessing.py b/tests/test_multiprocessing.py
--- a/tests/test_multiprocessing.py
+++ b/tests/test_multiprocessing.py
@@ -1,9 +1,11 @@
 import multiprocessing
 import random
 import signal
+import pickle
 import pytest
 
 from sanic.testing import HOST, PORT
+from sanic.response import text
 
 
 @pytest.mark.skipif(
@@ -27,3 +29,54 @@ def stop_on_alarm(*args):
     app.run(HOST, PORT, workers=num_workers)
 
     assert len(process_list) == num_workers
+
+
+def test_multiprocessing_with_blueprint(app):
+    from sanic import Blueprint
+    # Selects a number at random so we can spot check
+    num_workers = random.choice(range(2, multiprocessing.cpu_count() * 2 + 1))
+    process_list = set()
+
+    def stop_on_alarm(*args):
+        for process in multiprocessing.active_children():
+            process_list.add(process.pid)
+            process.terminate()
+
+    signal.signal(signal.SIGALRM, stop_on_alarm)
+    signal.alarm(3)
+
+    bp = Blueprint('test_text')
+    app.blueprint(bp)
+    app.run(HOST, PORT, workers=num_workers)
+
+    assert len(process_list) == num_workers
+
+
+# this function must be outside a test function so that it can be
+# able to be pickled (local functions cannot be pickled).
+def handler(request):
+    return text('Hello')
+
+# Muliprocessing on Windows requires app to be able to be pickled
+@pytest.mark.parametrize('protocol', [3, 4])
+def test_pickle_app(app, protocol):
+    app.route('/')(handler)
+    p_app = pickle.dumps(app, protocol=protocol)
+    up_p_app = pickle.loads(p_app)
+    assert up_p_app
+    request, response = app.test_client.get('/')
+    assert response.text == 'Hello'
+
+
+@pytest.mark.parametrize('protocol', [3, 4])
+def test_pickle_app_with_bp(app, protocol):
+    from sanic import Blueprint
+    bp = Blueprint('test_text')
+    bp.route('/')(handler)
+    app.blueprint(bp)
+    p_app = pickle.dumps(app, protocol=protocol)
+    up_p_app = pickle.loads(p_app)
+    assert up_p_app
+    request, response = app.test_client.get('/')
+    assert app.is_request_stream is False
+    assert response.text == 'Hello'