Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Branweb/python3 upgrade #15

Merged
merged 10 commits into from Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 2 additions & 3 deletions .TestDockerfile
@@ -1,10 +1,9 @@
FROM socrata/base-xenial
FROM socrata/python3-bionic

WORKDIR /app

RUN DEBIAN_FRONTEND=noninteractive apt-get -y update && \
DEBIAN_FRONTEND=noninteractive apt-get -y install python-mapnik python-pip
RUN pip install --upgrade pip
DEBIAN_FRONTEND=noninteractive apt-get -y install python3-mapnik

RUN mkdir -p /app/carto_renderer

Expand Down
10 changes: 6 additions & 4 deletions Dockerfile
@@ -1,16 +1,18 @@
FROM socrata/base-xenial
FROM socrata/python3-bionic

WORKDIR /app

RUN DEBIAN_FRONTEND=noninteractive apt-get -y update && \
DEBIAN_FRONTEND=noninteractive apt-get -y install python-mapnik python-pip
RUN pip install --upgrade pip
DEBIAN_FRONTEND=noninteractive apt-get -y install python3-mapnik

RUN mkdir -p /app/carto_renderer

ENV LOG_LEVEL INFO

ADD frozen.txt /app/
COPY bin/freeze-reqs.sh /app/
COPY dev-requirements.txt /app/
RUN chmod +x /app/freeze-reqs.sh
RUN /app/freeze-reqs.sh
RUN pip install -r /app/frozen.txt

COPY ship.d /etc/ship.d/
Expand Down
8 changes: 1 addition & 7 deletions README.md
Expand Up @@ -17,7 +17,7 @@ brew install mapnik

Install Mapnik (Debian/Ubuntu):
```
sudo apt-get install python-mapnik
sudo apt-get install python3-mapnik
```

Install Python Dependencies:
Expand All @@ -30,12 +30,6 @@ pip install -r dev-requirements.txt
bin/start-renderer.sh --dev
```

## Examples ##
Render an image to `test.png`:
```
curl -o test.png localhost:4096/render -H 'Content-type: application/json' -d @carto_renderer/examples/main.json
```

## Testing ##
The tests are run using py.test and hypothesis

Expand Down
1 change: 0 additions & 1 deletion bin/dockerize.sh
Expand Up @@ -3,6 +3,5 @@
set -e

cd "$(git rev-parse --show-toplevel 2>/dev/null)"
bin/freeze-reqs.sh
docker build --rm -t carto-renderer .

12 changes: 1 addition & 11 deletions bin/freeze-reqs.sh
Expand Up @@ -3,17 +3,7 @@
set -ev

# Change to the project root.
cd "$(git rev-parse --show-toplevel 2>/dev/null)"

VENV_DIR="venv"
if [ "${HUDSON_HOME}" ]; then
VENV_DIR="${HUDSON_HOME}/carto-renderer/${VENV_DIR}"
fi

if [ ! -d "${VENV_DIR}" ]; then
virtualenv "${VENV_DIR}"
fi
source "${VENV_DIR}"/bin/activate
cd /app

DEV_FILE='dev-requirements.txt'
FROZEN_FILE='frozen.txt'
Expand Down
9 changes: 4 additions & 5 deletions bin/start-renderer.sh
Expand Up @@ -11,20 +11,19 @@ if [ '--dev' = "$1" ]; then
fi
source venv/bin/activate

if [ ! -d venv/lib/python2.7/site-packages/mapnik ]; then
ln -s "$MAPNIK_DIR" venv/lib/python2.7/site-packages/
if [ ! -d venv/lib/python3.6/site-packages/mapnik ]; then
ln -s "$MAPNIK_DIR" venv/lib/python3.6/site-packages/
fi

pip install --upgrade --requirement dev-requirements.txt
PYTHONPATH=. carto_renderer/service.py
else
bin/dockerize.sh
rm frozen.txt


if [ 'Darwin' = "$(uname)" ]; then
OPTS=(-p 4096:4096 -e 'STYLE_HOST=docker.for.mac.localhost')
else
OPTS=('--net=host')
OPTS=('--net=host' -e 'STYLE_HOST=localhost')
fi

docker run "${OPTS[@]}" -e STYLE_PORT=4097 -e LOG_LEVEL=INFO carto-renderer
Expand Down
4 changes: 2 additions & 2 deletions carto_renderer/errors.py
Expand Up @@ -10,9 +10,9 @@ class ServiceError(Exception):
Base class for errors in this service.
"""
def __init__(self, message, status_code, request_body=None):
super(ServiceError, self).__init__(message)
self.status_code = status_code
self.request_body = request_body
self.message = message
logger = get_logger(self)
if request_body:
logger.error('Fatal Error (%d): "%s"; body: "%s"',
Expand Down Expand Up @@ -41,6 +41,6 @@ def __init__(self, keys, blob):
message = ''

beg = keys[:-1]
message = PayloadKeyError.msg.format("', '".join(beg), keys[-1])
message = PayloadKeyError.msg.format(b"', '".join(beg), keys[-1])

super(PayloadKeyError, self).__init__(message, 400, request_body=blob)
1 change: 0 additions & 1 deletion carto_renderer/examples/main-urlencode.mss

This file was deleted.

5 changes: 0 additions & 5 deletions carto_renderer/examples/main.json

This file was deleted.

7 changes: 0 additions & 7 deletions carto_renderer/examples/main.mss

This file was deleted.

1 change: 0 additions & 1 deletion carto_renderer/examples/min.mss

This file was deleted.

33 changes: 17 additions & 16 deletions carto_renderer/service.py
Expand Up @@ -4,9 +4,9 @@
"""

import json
from urllib import quote_plus
from urllib.parse import quote_plus

from tornado import web
from tornado import web, escape
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from tornado.ioloop import IOLoop
from tornado.options import define, parse_command_line, options
Expand Down Expand Up @@ -49,8 +49,7 @@ def render_png(tile, _zoom, xml, overscan):
box_max = TILE_SIZE + overscan - 1
map_tile.zoom_to_box(mapnik.Box2d(box_min, box_min, box_max, box_max))

for (name, features) in tile.items():
name = name.encode('ascii', 'ignore')
for (name, features) in list(tile.items()):
source = mapnik.MemoryDatasource()
map_layer = mapnik.Layer(name)
map_layer.datasource = source
Expand Down Expand Up @@ -103,10 +102,10 @@ def extract_body(self):
body = self.request.body

try:
extracted = msgpack.loads(body)
extracted = msgpack.loads(body, raw=True)
except Exception:
logger.warn('Invalid message')
raise BadRequest('Could not parse message.', body)
raise BadRequest('Could not parse message.', escape.to_unicode(body))
return extracted

def _handle_request_exception(self, err):
Expand All @@ -119,13 +118,14 @@ def _handle_request_exception(self, err):
logger.exception(err)
if isinstance(err, ServiceError):
status_code = err.status_code
payload['message'] = err.message
if err.request_body:
payload['request_body'] = err.request_body
else:
payload['message'] = str(err)
status_code = 500

payload['resultCode'] = status_code
payload['message'] = err.message

self.clear()
self.set_status(status_code)
Expand Down Expand Up @@ -168,16 +168,16 @@ class RenderHandler(BaseHandler):

Expects a dictionary with 'style', 'zoom', and 'tile' values.
"""
keys = ['tile', 'zoom', 'style']
keys = [b'tile', b'zoom', b'style']

def initialize(self, http_client, style_host, style_port):
"""Magic Tornado __init__ replacement."""
self.http_client = http_client # pragma: no cover
self.style_host = style_host # pragma: no cover
self.style_port = style_port # pragma: no cover

@web.asynchronous
def post(self):
# @web.asynchronous
async def post(self):
"""
Actually render the png.

Expand All @@ -192,15 +192,15 @@ def post(self):
raise PayloadKeyError(self.keys, geobody)
else:
try:
overscan = int(geobody['overscan'])
overscan = int(geobody[b'overscan'])
except:
logger.warn('Invalid JSON; overscan must be an integer: %s',
geobody)
raise BadRequest('"overscan" must be an integer',
request_body=geobody)

try:
zoom = int(geobody['zoom'])
zoom = int(geobody[b'zoom'])
except:
logger.warn('Invalid JSON; zoom must be an integer: %s',
geobody)
Expand All @@ -210,9 +210,9 @@ def post(self):
path = 'http://{host}:{port}/style?style={css}'.format(
host=self.style_host,
port=self.style_port,
css=quote_plus(geobody['style']))
css=quote_plus(geobody[b'style']))

tile = geobody['tile']
tile = geobody[b'tile']

def handle_response(response):
"""
Expand All @@ -228,7 +228,7 @@ def handle_response(response):

logger.info('zoom: %d, num features: %d, len(xml): %d',
zoom,
sum([len(layer) for layer in tile.values()]),
sum([len(layer) for layer in list(tile.values())]),
len(xml))
logger.debug('xml: %s',
LogWrapper.Lazy(lambda: xml.replace('\n', ' ')))
Expand All @@ -241,7 +241,8 @@ def handle_response(response):
else {}

req = HTTPRequest(path, headers=headers)
self.http_client.fetch(req, callback=handle_response)
resp = await self.http_client.fetch(req)
handle_response(resp)


def main(): # pragma: no cover
Expand Down