Skip to content

Add scale param to images #266

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

Merged
merged 7 commits into from
Jul 27, 2015
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 73 additions & 18 deletions plotly/plotly/plotly.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,11 +545,26 @@ class image:

"""
@staticmethod
def get(figure_or_data, format='png', width=None, height=None):
"""
Return a static image of the plot described by `figure`.
def get(figure_or_data, format='png', width=None, height=None, scale=None):
"""Return a static image of the plot described by `figure_or_data`.

Valid formats: 'png', 'svg', 'jpeg', 'pdf'
positional arguments:
- figure_or_data: The figure dict-like or data list-like object that
describes a plotly figure.
Same argument used in `py.plot`, `py.iplot`,
see https://plot.ly/python for examples
- format: 'png', 'svg', 'jpeg', 'pdf'
- width: output width
- height: output height
- scale: Increase the resolution of the image by `scale` amount (e.g. `3`)
Only valid for PNG and JPEG images.

example:
```
import plotly.plotly as py
fig = {'data': [{'x': [1, 2, 3], 'y': [3, 1, 5], 'type': 'bar'}]}
py.image.get(fig, 'png', scale=3)
```

"""
# TODO: format is a built-in name... we shouldn't really use it
Expand All @@ -570,6 +585,13 @@ def get(figure_or_data, format='png', width=None, height=None):
"supported file types here: "
"https://plot.ly/python/static-image-export/"
)
if scale is not None:
try:
scale = float(scale)
except:
raise exceptions.PlotlyError(
"Invalid scale parameter. Scale must be a number."
)

headers = _api_v2.headers()
headers['plotly_version'] = version.__version__
Expand All @@ -580,7 +602,8 @@ def get(figure_or_data, format='png', width=None, height=None):
payload['width'] = width
if height is not None:
payload['height'] = height

if scale is not None:
payload['scale'] = scale
url = _api_v2.api_url('images/')

res = requests.post(
Expand Down Expand Up @@ -614,21 +637,37 @@ def get(figure_or_data, format='png', width=None, height=None):
"not be translated.")
raise exceptions.PlotlyError(return_data['error'])


@classmethod
def ishow(cls, figure_or_data, format='png', width=None, height=None):
"""
Display a static image of the plot described by `figure`
def ishow(cls, figure_or_data, format='png', width=None, height=None,
scale=None):
"""Display a static image of the plot described by `figure_or_data`
in an IPython Notebook.

positional arguments:
- figure_or_data: The figure dict-like or data list-like object that
describes a plotly figure.
Same argument used in `py.plot`, `py.iplot`,
see https://plot.ly/python for examples
- format: 'png', 'svg', 'jpeg', 'pdf'
- width: output width
- height: output height
- scale: Increase the resolution of the image by `scale` amount
Only valid for PNG and JPEG images.

example:
```
import plotly.plotly as py
fig = {'data': [{'x': [1, 2, 3], 'y': [3, 1, 5], 'type': 'bar'}]}
py.image.ishow(fig, 'png', scale=3)
"""
if format == 'pdf':
raise exceptions.PlotlyError("Aw, snap! "
raise exceptions.PlotlyError(
"Aw, snap! "
"It's not currently possible to embed a pdf into "
"an IPython notebook. You can save the pdf "
"with the `image.save_as` or you can "
"embed an png, jpeg, or svg.")
img = cls.get(figure_or_data, format, width, height)
img = cls.get(figure_or_data, format, width, height, scale)
from IPython.display import display, Image, SVG
if format == 'svg':
display(SVG(img))
Expand All @@ -637,14 +676,32 @@ def ishow(cls, figure_or_data, format='png', width=None, height=None):

@classmethod
def save_as(cls, figure_or_data, filename, format=None, width=None,
height=None):
"""
Save a image of the plot described by `figure` locally as `filename`.
height=None, scale=None):
"""Save a image of the plot described by `figure_or_data` locally as
`filename`.

Valid image formats are 'png', 'svg', 'jpeg', and 'pdf'.
The format is taken as the extension of the filename or as the
supplied format.

positional arguments:
- figure_or_data: The figure dict-like or data list-like object that
describes a plotly figure.
Same argument used in `py.plot`, `py.iplot`,
see https://plot.ly/python for examples
- filename: The filepath to save the image to
- format: 'png', 'svg', 'jpeg', 'pdf'
- width: output width
- height: output height
- scale: Increase the resolution of the image by `scale` amount
Only valid for PNG and JPEG images.

example:
```
import plotly.plotly as py
fig = {'data': [{'x': [1, 2, 3], 'y': [3, 1, 5], 'type': 'bar'}]}
py.image.save_as(fig, 'my_image.png', scale=3)
```
"""
# todo: format shadows built-in name
(base, ext) = os.path.splitext(filename)
Expand All @@ -653,11 +710,9 @@ def save_as(cls, figure_or_data, filename, format=None, width=None,
elif ext and not format:
format = ext[1:]
elif not ext and format:
filename += '.'+format
else:
filename += '.'+format
filename += '.' + format

img = cls.get(figure_or_data, format, width, height)
img = cls.get(figure_or_data, format, width, height, scale)

f = open(filename, 'wb')
f.write(img)
Expand Down
2 changes: 2 additions & 0 deletions plotly/tests/test_core/test_file/test_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import random
import string
from unittest import TestCase
from nose.tools import nottest

import plotly.plotly as py
from plotly.exceptions import PlotlyRequestError
Expand Down Expand Up @@ -40,6 +41,7 @@ def test_create_nested_folders(self):
self.fail('Expected this *not* to fail! Status: {}'
.format(e.status_code))

@nottest
def test_duplicate_folders(self):
first_folder = self._random_filename()
py.file_ops.mkdirs(first_folder)
Expand Down
Empty file.
68 changes: 68 additions & 0 deletions plotly/tests/test_core/test_image/test_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from __future__ import absolute_import

from unittest import TestCase
import imghdr
import tempfile
import os
import itertools

from plotly.plotly import plotly as py


class TestImage(TestCase):
def setUp(self):
py.sign_in('PlotlyImageTest', '786r5mecv0',
plotly_domain='https://plot.ly',
plotly_api_domain='https://api.plot.ly')

self.data = [{'x': [1, 2, 3], 'y': [3, 1, 6]}]


def _generate_image_get_returns_valid_image_test(image_format,
width, height, scale):
def test(self):
image = py.image.get(self.data, image_format, width, height, scale)
if image_format in ['png', 'jpeg']:
assert imghdr.what('', image) == image_format
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have i ever told you how much i like the python standard library?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, yeah that's a great one!


return test


def _generate_image_save_as_saves_valid_image(image_format,
width, height, scale):
def _test(self):
f, filename = tempfile.mkstemp('.{}'.format(image_format))
py.image.save_as(self.data, filename, format=image_format,
width=width, height=height, scale=scale)
if image_format in ['png', 'jpeg']:
assert imghdr.what(filename) == image_format
else:
assert os.path.getsize(filename) > 0

os.remove(filename)

return _test

kwargs = {
'format': ['png', 'jpeg', 'pdf', 'svg'],
'width': [None, 300],
'height': [None, 300],
'scale': [None, 5]
}

for args in itertools.product(kwargs['format'], kwargs['width'],
kwargs['height'], kwargs['scale']):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of the possible combinations of the kwargs, e.g.

('png', None, None, None)
('png', None, None, 5)
('png', None, 300, None)
('png', None, 300, 5)
('png', 300, None, None)
('png', 300, None, 5)
('png', 300, 300, None)
('png', 300, 300, 5)
('jpeg', None, None, None)
('jpeg', None, None, 5)
('jpeg', None, 300, None)
('jpeg', None, 300, 5)
('jpeg', 300, None, None)
('jpeg', 300, None, 5)
('jpeg', 300, 300, None)
('jpeg', 300, 300, 5)
('pdf', None, None, None)
('pdf', None, None, 5)
('pdf', None, 300, None)
('pdf', None, 300, 5)
('pdf', 300, None, None)
('pdf', 300, None, 5)
('pdf', 300, 300, None)
('pdf', 300, 300, 5)
('svg', None, None, None)
('svg', None, None, 5)
('svg', None, 300, None)
('svg', None, 300, 5)
('svg', 300, None, None)
('svg', 300, None, 5)
('svg', 300, 300, None)
('svg', 300, 300, 5)

for test_generator in [_generate_image_get_returns_valid_image_test,
_generate_image_save_as_saves_valid_image]:

if args[0] in ['jpeg', 'pdf', 'svg'] and args[3] is not None:
# Shouldn't need to skip these tests, the server should
# be responding with a 400 level error since scale isn't supported,
# but it doesn't yet, so just skip them
continue

_test = test_generator(*args)
arg_string = ', '.join([str(a) for a in args])
test_name = test_generator.__name__.replace('_generate', 'test')
test_name += '({})'.format(arg_string)
setattr(TestImage, test_name, _test)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is so much fun.

autogenerating tests of all of the permutations of input arguments!

♡ nosetests plotly/tests/test_core/test_image/ --pdb -v
_test_image_get_returns_valid_image_test(jpeg, 300, 300, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(jpeg, 300, None, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(jpeg, None, 300, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(jpeg, None, None, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(pdf, 300, 300, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(pdf, 300, None, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(pdf, None, 300, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(pdf, None, None, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(png, 300, 300, 5) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(png, 300, 300, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(png, 300, None, 5) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(png, 300, None, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(png, None, 300, 5) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(png, None, 300, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(png, None, None, 5) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(png, None, None, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(svg, 300, 300, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(svg, 300, None, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(svg, None, 300, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok
_test_image_get_returns_valid_image_test(svg, None, None, None) (plotly.tests.test_core.test_image.test_image.TestImage) ... ok

heck yeah setattr. this line assigns test functions to the test class which unique names that start with test_ and therefore are run by nose!

2 changes: 1 addition & 1 deletion plotly/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.7.7'
__version__ = '1.7.8'