Skip to content

Commit 8adfb77

Browse files
authored
Merge pull request #1015 from ocefpaf/firefox_headless
Use firefox headless instead of phantomjs
2 parents 48aa49e + 86f87c2 commit 8adfb77

File tree

6 files changed

+98
-67
lines changed

6 files changed

+98
-67
lines changed

.github/CONTRIBUTING.md

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -44,32 +44,37 @@ Whenever possible, please also include a [short, self-contained code example](ht
4444

4545
First of all, thanks for your interest in contributing!
4646

47-
- If you are new to git/Github, please take check a few tutorials
48-
on [git](https://git-scm.com/docs/gittutorial) and [GitHub](https://guides.github.com/).
49-
- The basic workflow for contributing is:
50-
1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository
51-
2. [Clone](https://help.github.com/articles/cloning-a-repository/) the repository to create a local copy on your computer:
52-
```
53-
git clone [email protected]:${user}/folium.git
54-
cd folium
55-
```
56-
3. Create a branch for your changes
57-
```
58-
git checkout -b name-of-your-branch
59-
```
60-
4. Make change to your local copy of the folium repository
61-
5. Make sure the tests pass:
62-
* in the repository folder do `pip install -e .` (needed for notebook tests)
63-
* along with all the dependencies install `phantomjs` via `npm install -g phantomjs` or by downloading it from [here](http://phantomjs.org/download.html) and installing manually
64-
* run `python -m pytest tests`
65-
* resolve all errors
66-
6. Commit those changes
67-
```
68-
git add file1 file2 file3
69-
git commit -m 'a descriptive commit message'
70-
```
71-
7. Push your updated branch to your fork
72-
```
73-
git push origin name-of-your-branch
74-
```
75-
8. [Open a pull request](https://help.github.com/articles/creating-a-pull-request/) to the python-visualization/folium
47+
If you are new to git/Github, please take check a few tutorials
48+
on [git](https://git-scm.com/docs/gittutorial) and [GitHub](https://guides.github.com/).
49+
50+
The basic workflow for contributing is:
51+
52+
1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository
53+
2. [Clone](https://help.github.com/articles/cloning-a-repository/) the repository to create a local copy on your computer:
54+
```
55+
git clone [email protected]:${user}/folium.git
56+
cd folium
57+
```
58+
3. Create a branch for your changes
59+
```
60+
git checkout -b name-of-your-branch
61+
```
62+
4. Install the dependencies listed in `requirements.txt` and `requirements-dev.txt`.
63+
5. Install Firefox, download [geckodriver](https://github.com/mozilla/geckodriver/releases)
64+
and put it in the PATH.
65+
6. Make changes to your local copy of the folium repository
66+
7. Make sure the tests pass:
67+
* in the repository folder do `pip install -e .` (needed for notebook tests)
68+
* run `python -m pytest tests`
69+
* run `flake8 folium --max-line-length=120`
70+
* resolve all errors
71+
8. Commit those changes
72+
```
73+
git add file1 file2 file3
74+
git commit -m 'a descriptive commit message'
75+
```
76+
9. Push your updated branch to your fork
77+
```
78+
git push origin name-of-your-branch
79+
```
80+
10. [Open a pull request](https://help.github.com/articles/creating-a-pull-request/) to the python-visualization/folium

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ examples/results/*
1313
/codestyles/*.xml
1414
/_mac/*.xml
1515
/inspection/*.xml
16+
geckodriver.log

.travis.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ language: minimal
22

33
sudo: false
44

5+
env:
6+
- MOZ_HEADLESS=1
7+
8+
addons:
9+
firefox: latest
10+
511
env:
612
global:
713
- secure: "gRT413onDOvwgiHpNXRsiqo+ZZSjwwBpjZryQ9h6IqYw6cTN9YVivYF15uTMD//mZyFeHRz+F/7/0EG2z+UYIBKbgktiNMbie/KizwRBnCThGpcch1VeizkBkPluWSQXndXM6STkHvn0eZBZBBh0QdTm1qaI0babUmgZuWhrX38="
@@ -31,8 +37,13 @@ before_install:
3137
- conda update conda
3238
- conda config --remove channels defaults --force
3339
- conda config --add channels conda-forge --force
34-
- conda create --name TEST python=$PY phantomjs --file requirements.txt --file requirements-dev.txt
40+
- conda create --name TEST python=$PY --file requirements.txt --file requirements-dev.txt
3541
- source activate TEST
42+
# firefox headless driver
43+
- wget https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz -O geckodriver.tar.gz
44+
- mkdir geckodriver
45+
- tar -xzf geckodriver.tar.gz -C geckodriver
46+
- export PATH=$PATH:$PWD/geckodriver
3647

3748
- if [[ "$PY" == "2.7" ]]; then
3849
conda install mock ;

folium/folium.py

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@
77

88
from __future__ import (absolute_import, division, print_function)
99

10-
import os
1110
import time
1211
import warnings
1312

1413
from branca.element import CssLink, Element, Figure, JavascriptLink, MacroElement
1514

1615
from folium.map import FitBounds
1716
from folium.raster_layers import TileLayer
18-
from folium.utilities import _parse_size, _validate_location
17+
from folium.utilities import _parse_size, _tmp_html, _validate_location
1918

2019
from jinja2 import Environment, PackageLoader, Template
2120

@@ -202,10 +201,10 @@ class Map(MacroElement):
202201
zoomControl: {{this.zoom_control.__str__().lower()}},
203202
});
204203
{% if this.control_scale %}L.control.scale().addTo({{this.get_name()}});{% endif %}
205-
204+
206205
{% if this.objects_to_stay_in_front %}
207206
function objects_in_front() {
208-
{% for obj in this.objects_to_stay_in_front %}
207+
{% for obj in this.objects_to_stay_in_front %}
209208
{{ obj.get_name() }}.bringToFront();
210209
{% endfor %}
211210
};
@@ -290,35 +289,30 @@ def _repr_html_(self, **kwargs):
290289
def _to_png(self, delay=3):
291290
"""Export the HTML to byte representation of a PNG image.
292291
293-
Uses Phantom JS to render the HTML and record a PNG. You may need to
292+
Uses selenium to render the HTML and record a PNG. You may need to
294293
adjust the `delay` time keyword argument if maps render without data or tiles.
295294
296295
Examples
297296
--------
298297
>>> map._to_png()
299298
>>> map._to_png(time=10) # Wait 10 seconds between render and snapshot.
300-
"""
301299
300+
"""
302301
if self._png_image is None:
303-
import selenium.webdriver
302+
from selenium import webdriver
303+
304+
options = webdriver.firefox.options.Options()
305+
options.add_argument('--headless')
306+
driver = webdriver.Firefox(options=options)
304307

305-
driver = selenium.webdriver.PhantomJS(
306-
service_log_path=os.path.devnull
307-
)
308-
driver.get('about:blank')
309308
html = self.get_root().render()
310-
html = html.replace('\'', '"').replace('"', '\\"')
311-
html = html.replace('\n', '')
312-
driver.execute_script('document.write(\"{}\")'.format(html))
313-
driver.maximize_window()
314-
# Ignore user map size.
315-
# todo: fix this
316-
# driver.execute_script("document.body.style.width = '100%';") # noqa
317-
# We should probably monitor if some element is present,
318-
# but this is OK for now.
319-
time.sleep(delay)
320-
png = driver.get_screenshot_as_png()
321-
driver.quit()
309+
with _tmp_html(html) as fname:
310+
# We need the tempfile to avoid JS security issues.
311+
driver.get('file:///{path}'.format(path=fname))
312+
driver.maximize_window()
313+
time.sleep(delay)
314+
png = driver.get_screenshot_as_png()
315+
driver.quit()
322316
self._png_image = png
323317
return self._png_image
324318

folium/utilities.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import math
77
import os
88
import struct
9+
import tempfile
910
import zlib
11+
from contextlib import contextmanager
1012

1113
import numpy as np
1214

@@ -385,3 +387,17 @@ def iter_points(x):
385387
return [x]
386388
else:
387389
return []
390+
391+
392+
@contextmanager
393+
def _tmp_html(data):
394+
"""Yields the path of a temporary HTML file containing data."""
395+
filepath = ''
396+
try:
397+
fid, filepath = tempfile.mkstemp(suffix='.html', prefix='folium_')
398+
os.write(fid, data.encode('utf8'))
399+
os.close(fid)
400+
yield filepath
401+
finally:
402+
if os.path.isfile(filepath):
403+
os.remove(filepath)

tests/test_repr.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
from __future__ import (absolute_import, division, print_function)
1010

11-
import sys
1211
import io
12+
import sys
1313

1414
import PIL.Image
1515

@@ -19,18 +19,22 @@
1919

2020

2121
@pytest.fixture
22-
def make_map(png_enabled=False):
23-
m = folium.Map(png_enabled=png_enabled)
24-
return m
22+
def m():
23+
yield folium.Map(png_enabled=False)
24+
25+
26+
@pytest.fixture
27+
def m_png():
28+
yield folium.Map(png_enabled=True)
2529

2630

27-
def test__repr_html_is_str():
28-
html = make_map()._repr_html_()
31+
def test__repr_html_is_str(m):
32+
html = m._repr_html_()
2933
assert isinstance(html, str)
3034

3135

32-
def test_valid_html():
33-
html = make_map()._repr_html_()
36+
def test_valid_html(m):
37+
html = m._repr_html_()
3438
parts = html.split('><')
3539
assert len(parts) == 6
3640
assert parts[0].lstrip('<div ') == 'style="width:100%;"'
@@ -41,19 +45,19 @@ def test_valid_html():
4145
assert parts[5] == '/div>'
4246

4347

44-
def test__repr_png_no_image():
45-
png = make_map(png_enabled=False)._repr_png_()
48+
def test__repr_png_no_image(m):
49+
png = m._repr_png_()
4650
assert png is None
4751

4852

49-
def test__repr_png_is_bytes():
50-
png = make_map(png_enabled=True)._repr_png_()
53+
def test__repr_png_is_bytes(m_png):
54+
png = m_png._repr_png_()
5155
assert isinstance(png, bytes)
5256

5357

5458
@pytest.mark.skipif(sys.version_info < (3, 0),
5559
reason="Doesn't work on Python 2.7.")
56-
def test_valid_png():
57-
png = make_map(png_enabled=True)._repr_png_()
60+
def test_valid_png(m_png):
61+
png = m_png._repr_png_()
5862
img = PIL.Image.open(io.BytesIO(png))
5963
isinstance(img, PIL.PngImagePlugin.PngImageFile)

0 commit comments

Comments
 (0)