Merge pull request #259 from astrofrog/cleanup

Infrastructure cleanup
This commit is contained in:
Thomas Robitaille 2020-03-31 13:58:30 +01:00 коммит произвёл GitHub
Родитель fb7f8110cb d582c3e879
Коммит 9775b08778
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
36 изменённых файлов: 351 добавлений и 283 удалений

Просмотреть файл

@ -14,6 +14,8 @@
- Incorporate time series behavior for data layers; add method that
returns current time in the viewer. [#187]
- Drop support for Python 2.7 and now require at least Python 3.6. [#259]
0.7.0 (2019-09-20)
------------------

Просмотреть файл

@ -1,17 +1,38 @@
resources:
repositories:
- repository: OpenAstronomy
type: github
endpoint: WorldWideTelescope
name: OpenAstronomy/azure-pipelines-templates
ref: master
jobs:
- template: azure-template.yml
- template: run-tox-env.yml@OpenAstronomy
parameters:
name: Linux
os: linux
- template: azure-template.yml
parameters:
name: Windows
os: windows
xvfb: true
coverage: codecov
libraries:
apt:
- libxkbcommon-x11-0
- libgl1-mesa-dev
# Don't test on MacOS X for now
# - template: azure-template.yml
# parameters:
# name: MacOSX
# os: macosx
envs:
- linux: codestyle
libraries: {}
coverage: 'false'
- linux: py36-test
- linux: py37-test
- windows: py36-test
- windows: py37-test
# - macos: py36-test
# - macos: py37-test
- linux: py36-docs
- windows: py37-docs
- macos: py37-docs

Просмотреть файл

@ -1,84 +0,0 @@
parameters:
python_versions: ['27', '36', '37']
jobs:
- ${{ each python_version in parameters.python_versions }}:
- ${{ if or(ne(parameters.os, 'windows'), ne(python_version, '27')) }}:
- job: ${{ parameters.name }}_${{ python_version }}
pool:
${{ if eq(parameters.os, 'macosx') }}:
vmImage: macOS 10.13
${{ if eq(parameters.os, 'linux') }}:
vmImage: Ubuntu 16.04
${{ if eq(parameters.os, 'windows') }}:
vmImage: vs2017-win2016
steps:
- ${{ if eq(parameters.os, 'linux') }}:
- bash: |
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid \
--make-pidfile --background --exec /usr/bin/Xvfb \
-- :99 -screen 0 1920x1200x24 -ac \
+extension GLX +render -noreset
displayName: Starting Xvfb
- ${{ if eq(python_version, '27') }}:
- task: CondaEnvironment@1
inputs:
packageSpecs: 'python=2.7 pyqt'
updateConda: true
cleanEnvironment: true
- ${{ if ne(python_version, '27') }}:
- task: UsePythonVersion@0
inputs:
${{ if eq(python_version, '36') }}:
versionSpec: '3.6'
${{ if eq(python_version, '37') }}:
versionSpec: '3.7'
architecture: 'x64'
- bash: python -m pip install "PyQt5==5.9.*"
displayName: Installing PyQt5
- bash: python -m pip install .[test,lab] pyopengl
displayName: Installing PyWWT and dependencies
- bash: python -c "import pywwt"
displayName: Test basic PyWWT importability
- bash: jupyter nbextension list
displayName: Listing Jupyter Notebook extensions
- bash: jupyter labextension list
displayName: Listing Jupyter Lab extensions
- bash: jupyter serverextension list
displayName: Listing Jupyter Server extensions
- bash: python .check_enabled.py
displayName: Checking that plugins are enabled
- ${{ if ne(parameters.os, 'macosx') }}:
- bash: python -m pytest pywwt --cov pywwt
displayName: Running tests
env:
DISPLAY: :99.0
CI: true
- bash: python -m pip install codecov
displayName: Installing codecov
- bash: python -m codecov --name ${{ parameters.name }}_${{ python_version }} -t $(codecov.token)
displayName: Running codecov
- bash: python -m pip install .[docs]
displayName: Installing documentation dependencies
- bash: make -C docs html linkcheck
displayName: Building docs

Просмотреть файл

@ -60,7 +60,7 @@ If you install pywwt using pip or conda as described above, any required
dependencies will get installed automatically (with the exception of PyQt/PySide
if using pip). For the record, these dependencies are as follows:
* `Python <https://www.python.org>`_ 2.7, or 3.5 or later
* `Python <https://www.python.org>`_ 3.6 or later
* `NumPy <https://www.numpy.org>`_ 1.9 or later
* `Matplotlib <https://matplotlib.org>`_ 1.5 or later
* `Astropy <https://www.astropy.org>`_ 1.0 or later
@ -72,7 +72,6 @@ if using pip). For the record, these dependencies are as follows:
* `ipyevents <https://github.com/mwcraig/ipyevents>`_
* `traitlets <https://traitlets.readthedocs.io>`_
* `reproject <https://reproject.readthedocs.io/>`_
* `six <https://six.readthedocs.io/>`_
* `pytz <http://pythonhosted.org/pytz>`_
In addition, if you want to use the Qt widget, you will need:
@ -82,8 +81,7 @@ In addition, if you want to use the Qt widget, you will need:
<https://riverbankcomputing.com/software/pyqtwebengine/intro>`__ (both PyQt4
and PyQt5 are supported)
* `QtPy <https://pypi.org/project/QtPy/>`__ 1.2 or later
* `flask <https://palletsprojects.com/p/flask/>`_
* `flask-cors <https://github.com/corydolphin/flask-cors>`_
* `tornado <https://www.tornadoweb.org/en/stable/>`_
For the Jupyter widget, you will need:

Просмотреть файл

@ -6,5 +6,5 @@ __version__ = '%s.%s.%s%s' % (
version_info[0],
version_info[1],
version_info[2],
'' if version_info[3]=='final' else _specifier_[version_info[3]]+str(version_info[4])
'' if version_info[3] == 'final' else _specifier_[version_info[3]]+str(version_info[4])
)

Просмотреть файл

@ -2,7 +2,6 @@ import uuid
from traitlets import HasTraits, TraitError, validate
from astropy import units as u
from astropy.coordinates import concatenate, SkyCoord
import requests
import numpy as np
from .traits import (Color, ColorWithOpacity, Bool,
@ -164,7 +163,7 @@ class Circle(Annotation):
def _serialize_state(self):
state = super(Circle, self)._serialize_state()
state['settings']['skyRelative'] = self.radius.unit.is_equivalent(u.degree)
state['settings']['skyRelative'] = self.radius.unit.is_equivalent(u.degree)
state['center'] = {'ra': self._center.ra.deg,
'dec': self._center.dec.deg}
return state

Просмотреть файл

@ -16,5 +16,6 @@ def get_qapp():
def cleanup_qapp():
global app
app.quit()
app = None
if app is not None:
app.exit()
app = None

Просмотреть файл

@ -42,6 +42,7 @@ if QT_INSTALLED and OPENGL_INSTALLED:
_cached_opengl_renderer = ''
def pytest_report_header(config):
global _cached_opengl_renderer
@ -88,7 +89,6 @@ def pytest_report_header(config):
def pytest_unconfigure(config):
if QT_INSTALLED:
from .app import cleanup_qapp
cleanup_qapp()

Просмотреть файл

@ -3,7 +3,6 @@ from traitlets import HasTraits, observe, validate, TraitError
from astropy import units as u
from astropy.time import Time
from astropy.coordinates import SkyCoord
from datetime import datetime
# We import the trait classes from .traits since we do various customizations
from .traits import Color, Bool, Float, Unicode, AstropyQuantity
@ -16,7 +15,6 @@ from .instruments import Instruments
from .utils import ensure_utc
import json
import os
import shutil
import tempfile
@ -128,11 +126,17 @@ class BaseWWTWidget(HasTraits):
help='Whether to only show boundaries for '
'the selected constellation '
'(`bool`)').tag(wwt='showConstellationSelection', wwt_reset=True)
#constellation_pictures = Bool(False, help='Whether to show pictures of the constellations\' mythological representations (`bool`)').tag(wwt='showConstellationPictures', wwt_reset=True)
#constellation_labels = Bool(False, help='Whether to show labelss for constellations (`bool`)').tag(wwt='showConstellationLabels', wwt_reset=True)
# constellation_pictures = Bool(False,
# help='Whether to show pictures of the constellations\' '
# 'mythological representations '
# '(`bool`)').tag(wwt='showConstellationPictures', wwt_reset=True)
# constellation_labels = Bool(False,
# help='Whether to show labelss for constellations '
# '(`bool`)').tag(wwt='showConstellationLabels', wwt_reset=True)
crosshairs = Bool(False, help='Whether to show crosshairs at the center of '
'the field (`bool`)').tag(wwt='showCrosshairs', wwt_reset=True)
'the field (`bool`)').tag(wwt='showCrosshairs', wwt_reset=True)
crosshairs_color = Color('white',
help='The color of the crosshairs '
'(`str` or `tuple`)').tag(wwt='crosshairsColor', wwt_reset=True)
@ -260,10 +264,14 @@ class BaseWWTWidget(HasTraits):
'in the viewer (`bool`)').tag(wwt='galacticMode', wwt_reset=True)
galactic_grid = Bool(False, help='Whether to show a grid relative to the '
'galactic plane (`bool`)').tag(wwt='showGalacticGrid', wwt_reset=True)
#galactic_text = Bool(False, help='Whether to show labels for the galactic grid\'s text (`bool`)').tag(wwt='showGalacticGridText', wwt_reset=True)
# galactic_text = Bool(False,
# help='Whether to show labels for the galactic grid\'s text '
# '(`bool`)').tag(wwt='showGalacticGridText', wwt_reset=True)
alt_az_grid = Bool(False, help='Whether to show an altitude-azimuth grid '
'(`bool`)').tag(wwt='showAltAzGrid', wwt_reset=True)
#alt_az_text = Bool(False, help='Whether to show labels for the altitude-azimuth grid\'s text (`bool`)').tag(wwt='showAltAzGridText', wwt_reset=True)
# alt_az_text = Bool(False,
# help='Whether to show labels for the altitude-azimuth grid\'s text '
# '(`bool`)').tag(wwt='showAltAzGridText', wwt_reset=True)
local_horizon_mode = Bool(False, help='Whether the view should be that of '
'a local latitude, longitude, and '
@ -274,8 +282,8 @@ class BaseWWTWidget(HasTraits):
'(:class:`~astropy.units.Quantity`)').tag(wwt='locationAltitude', wwt_reset=True)
location_latitude = AstropyQuantity(47.633 * u.deg,
help='The latitude of the viewing '
'location in local horizon mode '
'(:class:`~astropy.units.Quantity`)').tag(wwt='locationLat', wwt_reset=True)
'location in local horizon mode '
'(:class:`~astropy.units.Quantity`)').tag(wwt='locationLat', wwt_reset=True)
location_longitude = AstropyQuantity(122.133333 * u.deg,
help='The longitude of the viewing '
'location in local horizon mode '
@ -589,7 +597,7 @@ class BaseWWTWidget(HasTraits):
If left blank, the WWT viewport will fill the enitre height of the browser.
"""
dest_root, dest_extension = os.path.splitext(dest)
if (dest_extension and dest_extension != ".zip"):
if (dest_extension and dest_extension != ".zip"):
raise ValueError("'dest' must be either a directory or a .zip file")
is_compressed = dest_extension == '.zip'
@ -610,10 +618,10 @@ class BaseWWTWidget(HasTraits):
shutil.copy(os.path.join(nbexten_dir, 'wwt_json_api.js'), script_dir)
shutil.copy(os.path.join(fig_src_dir, "interactive_figure.js"), script_dir)
self._serialize_to_json(os.path.join(figure_dir,'wwt_figure.json'), title, max_width, max_height)
self._serialize_to_json(os.path.join(figure_dir, 'wwt_figure.json'), title, max_width, max_height)
if len(self.layers) > 0:
data_dir = os.path.join(figure_dir,'data')
data_dir = os.path.join(figure_dir, 'data')
if not os.path.exists(data_dir):
os.mkdir(data_dir)
self._save_added_data(data_dir)
@ -662,8 +670,8 @@ class BaseWWTWidget(HasTraits):
def _serialize_to_json(self, file, title, max_width, max_height):
state = self._serialize_state(title, max_width, max_height)
with open(file,'w') as file_obj:
json.dump(state,file_obj)
with open(file, 'w') as file_obj:
json.dump(state, file_obj)
def _save_added_data(self, dir):
self.layers._save_all_data_for_serialization(dir)

Просмотреть файл

@ -1,5 +1,6 @@
import os
import time
import asyncio
import socket
import logging
from hashlib import md5
@ -12,7 +13,7 @@ _data_server = None
def get_data_server(verbose=True):
"""
This starts up a flask server and returns a handle to a DataServer
This starts up a tornado server and returns a handle to a DataServer
object which can be used to register files to serve.
"""
@ -21,32 +22,28 @@ def get_data_server(verbose=True):
if _data_server is not None:
return _data_server
from flask import Flask
from flask_cors import CORS
from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application
from tornado.routing import PathMatches
class FlaskWrapper(Flask):
class WebServer(Application):
port = None
host = None
port = None
def run(self, *args, **kwargs):
self.host = kwargs.get('host', None)
self.port = kwargs.get('port', None)
def run(self, host=None, port=8886):
self.host = host
self.port = port
try:
super(FlaskWrapper, self).run(*args, **kwargs)
self.listen(port)
IOLoop.instance().start()
finally:
self.host = None
self.port = None
app = FlaskWrapper('DataServer')
CORS(app)
if verbose:
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
class DataServer(object):
def __init__(self):
def start(self, app):
self._files = {}
self._thread = Thread(target=self.start_app)
self._thread.daemon = True
@ -64,15 +61,22 @@ def get_data_server(verbose=True):
return self._app.host
def start_app(self):
host = socket.gethostbyname('localhost')
for port in range(8000, 9000):
try:
return app.run(host=host, port=port)
except Exception:
pass
raise Exception("Could not start up data server")
def serve_file(self, filename, real_name=False, extension=''):
asyncio.set_event_loop(asyncio.new_event_loop())
host = socket.gethostbyname('localhost')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 0))
port = sock.getsockname()[1]
sock.close()
access_log = logging.getLogger("tornado.access")
access_log.setLevel('ERROR')
self._app.run(host=host, port=port)
def serve_file(self, filename, real_name=True, extension=''):
with open(filename, 'rb') as f:
content = f.read()
if real_name:
@ -88,9 +92,13 @@ def get_data_server(verbose=True):
ds = DataServer()
@app.route("/data/<hash>")
def data(hash):
return ds.get_file_contents(hash)
class DataHandler(RequestHandler):
async def get(self, hash):
self.write(ds.get_file_contents(hash))
app = WebServer([(PathMatches(r"/data/(?P<hash>\S+)"), DataHandler)])
ds.start(app)
_data_server = ds

Просмотреть файл

@ -60,35 +60,35 @@ class ImageryLayers():
# (og_list) into a dict.
for layer in og_list:
if re.search(r'(?i)gamma',layer) is not None:
if re.search(r'(?i)gamma', layer) is not None:
self._add2dict(self._layers, layer, 'gamma')
continue # automatically advance to next iteration
continue # automatically advance to next iteration
if re.search(r'(?i)x(-|\s)?ray',layer) is not None:
if re.search(r'(?i)x(-|\s)?ray', layer) is not None:
self._add2dict(self._layers, layer, 'x')
continue
if (re.search(r'(?i)ultra(-|\s)?violet',layer) is not None or
re.search(r'(?i)[^\d\w]+uv|uv[^\d\w]+',layer) is not None):
if (re.search(r'(?i)ultra(-|\s)?violet', layer) is not None or
re.search(r'(?i)[^\d\w]+uv|uv[^\d\w]+', layer) is not None):
self._add2dict(self._layers, layer, 'uv')
continue
if (re.search(r'(?i)optical',layer) is not None or
re.search(r'(?i)visible',layer) is not None):
if (re.search(r'(?i)optical', layer) is not None or
re.search(r'(?i)visible', layer) is not None):
self._add2dict(self._layers, layer, 'visible')
continue
if (re.search(r'(?i)infrared',layer) is not None or
re.search(r'(?i)[^\d\w]+ir|ir[^\d\w]+',layer) is not None):
if (re.search(r'(?i)infrared', layer) is not None or
re.search(r'(?i)[^\d\w]+ir|ir[^\d\w]+', layer) is not None):
self._add2dict(self._layers, layer, 'ir')
continue
if (re.search(r'(?i)microwave',layer) is not None or
re.search(r'(?i)[^\d\w]+cmb|cmb[^\d\w]+',layer) is not None):
if (re.search(r'(?i)microwave', layer) is not None or
re.search(r'(?i)[^\d\w]+cmb|cmb[^\d\w]+', layer) is not None):
self._add2dict(self._layers, layer, 'micro')
continue
if re.search(r'(?i)radio',layer) is not None:
if re.search(r'(?i)radio', layer) is not None:
self._add2dict(self._layers, layer, 'radio')
continue

Просмотреть файл

@ -1,5 +1,6 @@
import requests
class Instruments():
"""
A supplemental class that enables tab-completion for available

Просмотреть файл

@ -3,14 +3,10 @@
# because we instead use JSON messages to transmit any changes between the
# Python and Javascript parts so that we can re-use this for the Qt client.
import sys
PY2 = sys.version_info[0] == 2
import ipywidgets as widgets
from traitlets import Unicode, Float, default, link, directional_link
if not PY2:
from ipyevents import Event as DOMListener
from ipyevents import Event as DOMListener
from .core import BaseWWTWidget
from .layers import ImageLayer
@ -21,8 +17,7 @@ __all__ = ['WWTJupyterWidget']
VIEW_MODULE_VERSION = '0.8.0' # synchronize these with lib/wwt.js
MODEL_MODULE_VERSION = '0.8.0'
if not PY2:
dom_listener = DOMListener()
dom_listener = DOMListener()
@widgets.register
@ -48,10 +43,9 @@ class WWTJupyterWidget(widgets.DOMWidget, BaseWWTWidget):
def __init__(self):
widgets.DOMWidget.__init__(self)
BaseWWTWidget.__init__(self)
if not PY2:
dom_listener.source = self
dom_listener.prevent_default_action = True
dom_listener.watched_events = ['wheel']
dom_listener.source = self
dom_listener.prevent_default_action = True
dom_listener.watched_events = ['wheel']
self._controls = None
@default('layout')
@ -107,7 +101,7 @@ class JupyterImageLayer(ImageLayer):
@property
def controls(self):
from .layers import VALID_STRETCHES, VALID_COLORMAPS, UI_COLORMAPS
from .layers import VALID_STRETCHES, UI_COLORMAPS
if self._controls is not None:
return self._controls

Просмотреть файл

@ -88,7 +88,7 @@ def _compute_notebook_server_base_url():
for s in list_running_servers():
response = requests.get(
requests.compat.urljoin(s['url'], 'api/sessions'),
params = {'token': s.get('token', '')}
params={'token': s.get('token', '')}
)
for n in json.loads(response.text):
@ -100,6 +100,7 @@ def _compute_notebook_server_base_url():
_server_base_url = None
def get_notebook_server_base_url():
"""Get the "base_url" of the current Jupyter notebook server.
@ -144,9 +145,9 @@ def load_jupyter_server_extension(nb_server_app):
with open(CONFIG, 'w') as f:
json.dump(config, f)
mimetypes.add_type('image/fits','.fits')
mimetypes.add_type('image/fits','.fts')
mimetypes.add_type('image/fits','.fit')
mimetypes.add_type('image/fits', '.fits')
mimetypes.add_type('image/fits', '.fts')
mimetypes.add_type('image/fits', '.fit')
route_pattern = url_path_join(web_app.settings['base_url'], '/wwt/(.*)')
web_app.add_handlers(host_pattern, [(route_pattern, WWTFileHandler)])

Просмотреть файл

@ -24,7 +24,6 @@ from astropy.table import Column
from astropy.time import Time
from datetime import datetime
from ipywidgets import HBox, Dropdown, FloatText, FloatSlider, link
from traitlets import HasTraits, validate, observe
from .traits import Color, Bool, Float, Unicode, AstropyQuantity, Any, to_hex
from .utils import sanitize_image, validate_traits, ensure_utc
@ -81,7 +80,7 @@ UI_COLORMAPS = OrderedDict([
# Save string types for validating ISOT strings in time series tables
if sys.version_info[0] == 2:
STR_TYPE = basestring
STR_TYPE = basestring # noqa
NP_STR_TYPE = np.string_
else:
STR_TYPE = str
@ -176,7 +175,7 @@ def csv_table_win_newline(table):
s = StringIO()
table.write(s, format='ascii.basic', delimiter=',', comment=False)
s.seek(0)
#Replace single \r or \n characters with \r\n
# Replace single \r or \n characters with \r\n
return re.sub(r"(?<![\r\n])(\r|\n)(?![\r\n])", "\r\n", s.read())
@ -299,7 +298,7 @@ class LayerManager(object):
return layer_states
def _save_all_data_for_serialization (self, dir):
def _save_all_data_for_serialization(self, dir):
for layer in self._layers:
layer._save_data_for_serialization(dir)
@ -506,7 +505,6 @@ class TableLayer(HasTraits):
else:
raise ValueError('alt_type should be one of {0}'.format('/'.join(str(x) for x in VALID_ALT_TYPES)))
@validate('time_att')
def _check_time_att(self, proposal):
# Parse the time_att column and make sure it's in the proper format
@ -514,11 +512,11 @@ class TableLayer(HasTraits):
col = self.table[proposal['value']]
if (all(isinstance(t, datetime) for t in col)
or all(isinstance(t, Time) for t in col)):
or all(isinstance(t, Time) for t in col)):
return proposal['value']
elif (isinstance(col, STR_TYPE)
or np.issubdtype(col.dtype, NP_STR_TYPE)):
or np.issubdtype(col.dtype, NP_STR_TYPE)):
try:
Time(col, format='isot')
@ -753,7 +751,7 @@ class TableLayer(HasTraits):
@observe('time_att')
def _on_time_att_change(self, *value):
if len(self.time_att) == 0 or self.time_series == False:
if len(self.time_att) == 0 or self.time_series is False:
self.parent._send_msg(event='table_layer_set', id=self.id,
setting='startDateColumn', value=-1)
return
@ -883,9 +881,9 @@ class TableLayer(HasTraits):
return state
def _save_data_for_serialization(self, dir):
file_path = path.join(dir,"{0}.csv".format(self.id))
file_path = path.join(dir, "{0}.csv".format(self.id))
table_str = csv_table_win_newline(self.table)
with open(file_path, 'wb') as file: # binary mode to preserve windows line endings
with open(file_path, 'wb') as file: # binary mode to preserve windows line endings
file.write(table_str.encode('ascii', errors='replace'))
def __str__(self):
@ -1030,7 +1028,8 @@ class ImageLayer(HasTraits):
'settings': {}
}
#A bit overkill for just the opacity, but more future-proof in case we add more wwt traits
# A bit overkill for just the opacity, but more future-proof in case
# we add more wwt traits
for trait in self.traits().values():
wwt_name = trait.metadata.get('wwt')
if wwt_name:
@ -1047,8 +1046,8 @@ class ImageLayer(HasTraits):
return state
def _save_data_for_serialization(self, dir):
file_path = path.join(dir,"{0}.fits".format(self.id))
shutil.copyfile(self._sanitized_image,file_path)
file_path = path.join(dir, "{0}.fits".format(self.id))
shutil.copyfile(self._sanitized_image, file_path)
def __str__(self):
return 'ImageLayer'

Просмотреть файл

@ -5,4 +5,3 @@ def _jupyter_nbextension_paths():
'dest': 'pywwt',
'require': 'pywwt/extension'
}]

Просмотреть файл

@ -220,7 +220,7 @@ function wwt_apply_json_message(wwt, msg) {
// order.
if (msg['version'] > layer._stretch_version) {
layer.setImageScale(msg['stretch'], msg['vmin'], msg['vmax']);
layer.setImageScalePhysical(msg['stretch'], msg['vmin'], msg['vmax']);
layer._stretch_version = msg['version'];
layer.getFitsImage().transparentBlack = false;
}
@ -329,7 +329,7 @@ function wwt_apply_json_message(wwt, msg) {
} else if(name == 'color') {
value = wwtlib.Color.fromHex(msg['value']);
} else if(name == 'colorMapper') {
value = wwtlib.ColorMapContainer.fromNestedLists(msg['value']);
value = wwtlib.ColorMapContainer.fromArgbList(msg['value']);
} else if(name == 'altUnit') {
value = wwtlib.AltUnits[msg['value']];
} else if(name == 'raUnits') {

Просмотреть файл

@ -16,7 +16,7 @@ class SolarSystem(HasTraits):
super(SolarSystem, self).__init__()
self.base_widget = base_wwt_widget
self.observe(self._on_trait_change, type='change')
self._tracked_obj_id = 0 #Default to tracking sun
self._tracked_obj_id = 0 # Default to tracking sun
def _on_trait_change(self, changed):
# This method gets called anytime a trait gets changed. Since this class
@ -31,21 +31,23 @@ class SolarSystem(HasTraits):
new_value = new_value.value
self.base_widget._send_msg(event='setting_set',
setting=wwt_name,
value=new_value)
setting=wwt_name,
value=new_value)
#cmb = Bool(False, help='Whether to show the cosmic microwave background in solar system mode (`bool`)').tag(wwt='solarSystemCMB') ###
cosmos = Bool(True, help='Whether to show data from the SDSS survey (`bool`)').tag(wwt='solarSystemCosmos') ###
#display = Bool(False, help='Whether to show the solar system while in solar system mode (`bool`)').tag(wwt='solarSystemOverlays') ###
# cmb = Bool(False, help='Whether to show the cosmic microwave background in solar system mode (`bool`)').tag(wwt='solarSystemCMB')
cosmos = Bool(True, help='Whether to show data from the SDSS survey (`bool`)').tag(wwt='solarSystemCosmos')
# display = Bool(False, help='Whether to show the solar system while in solar system mode (`bool`)').tag(wwt='solarSystemOverlays')
lighting = Bool(True,
help='Whether to show the lighting effect of the Sun on the'
' solar system (`bool`)').tag(wwt='solarSystemLighting')
milky_way = Bool(True, help='Whether to show the galactic bulge in the '
'background in solar system mode '
'(`bool`)').tag(wwt='solarSystemMilkyWay')
#multi_res = Bool(False, help='Whether to show the multi-resolution textures for planets where available (`bool`)').tag(wwt='solarSystemMultiRes') ###
minor_orbits = Bool(False, help='Whether to show the orbits of minor planets in solar system mode (`bool`)').tag(wwt='solarSystemMinorOrbits')
#minor_planets = Bool(False, help='Whether to show minor planets in solar system mode (`bool`)').tag(wwt='solarSystemMinorPlanets') ###
# multi_res = Bool(False, help='Whether to show the multi-resolution textures for planets where available (`bool`)').tag(wwt='solarSystemMultiRes')
minor_orbits = Bool(False,
help='Whether to show the orbits of minor planets '
'in solar system mode (`bool`)').tag(wwt='solarSystemMinorOrbits')
# minor_planets = Bool(False, help='Whether to show minor planets in solar system mode (`bool`)').tag(wwt='solarSystemMinorPlanets')
orbits = Bool(True,
help='Whether to show orbit paths when the solar system is '
'displayed (`bool`)').tag(wwt='solarSystemOrbits')

Двоичные данные
pywwt/tests/data/webengine/image_layer_equ.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 3.4 KiB

После

Ширина:  |  Высота:  |  Размер: 5.5 KiB

Двоичные данные
pywwt/tests/data/webengine/image_layer_gal.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 18 KiB

После

Ширина:  |  Высота:  |  Размер: 32 KiB

Двоичные данные
pywwt/tests/data/webengine/sky_layers.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 28 KiB

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 3.4 KiB

После

Ширина:  |  Высота:  |  Размер: 5.5 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 18 KiB

После

Ширина:  |  Высота:  |  Размер: 32 KiB

Двоичные данные
pywwt/tests/data/webengine_osx/sky_layers.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 28 KiB

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Двоичные данные
pywwt/tests/data/webkit/image_layer_equ.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 3.4 KiB

После

Ширина:  |  Высота:  |  Размер: 5.5 KiB

Двоичные данные
pywwt/tests/data/webkit/image_layer_gal.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 18 KiB

После

Ширина:  |  Высота:  |  Размер: 32 KiB

Двоичные данные
pywwt/tests/data/webkit/sky_layers.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 28 KiB

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Двоичные данные
pywwt/tests/data/webkit_win/image_layer_equ.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 3.4 KiB

После

Ширина:  |  Высота:  |  Размер: 5.5 KiB

Двоичные данные
pywwt/tests/data/webkit_win/image_layer_gal.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 18 KiB

После

Ширина:  |  Высота:  |  Размер: 32 KiB

Двоичные данные
pywwt/tests/data/webkit_win/sky_layers.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 28 KiB

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Просмотреть файл

@ -178,7 +178,7 @@ class TestLayers:
self.table['flux'].unit = 'm'
layer = self.client.layers.add_table_layer(table=self.table,
lon_att='ra', lat_att='dec', alt_att='flux')
lon_att='ra', lat_att='dec', alt_att='flux')
assert layer.lon_att == 'ra'
assert layer.lon_unit is u.deg
@ -213,31 +213,28 @@ class TestLayers:
assert layer.alt_att == ''
def test_line_endings(self):
self.table['ra'] = [1,2,3]
self.table['ra'] = [1, 2, 3]
expected_str = "flux,dec,ra\r\n"\
"2,4,1\r\n"\
"3,5,2\r\n"\
"4,6,3\r\n"
"2,4,1\r\n"\
"3,5,2\r\n"\
"4,6,3\r\n"
assert csv_table_win_newline(self.table) == expected_str
def test_deprecated_api_call(self, capsys):
def test_deprecated_api_call(self):
"""For the time being, test that the deprecated name for this function still
works, but issues a warning
"""
import warnings
assert len(self.client.layers) == 0
assert str(self.client.layers) == 'Layer manager with no layers'
with warnings.catch_warnings(record=True) as w:
layer1 = self.client.layers.add_data_layer(table=self.table)
with pytest.warns(DeprecationWarning):
self.client.layers.add_data_layer(table=self.table)
assert len(self.client.layers) == 1
assert str(self.client.layers) == ('Layer manager with 1 layers:\n\n'
' [0]: TableLayer with 3 markers\n')
assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning)
def test_cartesian_layer(self):
@ -320,7 +317,7 @@ def test_table_layers_image(tmpdir, wwt_qt_client):
table['dec'] = [84, 85, 86, 87, 88]
table['ra'] = [250, 260, 270, 280, 290] * u.deg
layer1 = wwt.layers.add_table_layer(table=table)
layer1 = wwt.layers.add_table_layer(table=table) # noqa
# Case where we change the default values on initialization
@ -330,7 +327,7 @@ def test_table_layers_image(tmpdir, wwt_qt_client):
table['ra'] = [250, 260, 270, 280, 290] * u.deg
table['other'] = [255, 265, 275, 285, 295] * u.deg
layer2 = wwt.layers.add_table_layer(table=table, color='red', lon_att='other', size_scale=100, opacity=0.5)
layer2 = wwt.layers.add_table_layer(table=table, color='red', lon_att='other', size_scale=100, opacity=0.5) # noqa
# Case where we change the values after initialization
@ -357,7 +354,7 @@ def test_table_layers_image(tmpdir, wwt_qt_client):
table['ra'] = [250, 260, 270, 280, 290] * u.deg
table['other'] = [255, 265, 275, 285, 295] * u.deg
layer4 = wwt.layers.add_table_layer(table=table, cmap_att='other', size_att='flux')
layer4 = wwt.layers.add_table_layer(table=table, cmap_att='other', size_att='flux', size_scale=100) # noqa
# Case with size and color encoding where we change the values after initialization
@ -373,6 +370,7 @@ def test_table_layers_image(tmpdir, wwt_qt_client):
layer5.cmap_att = 'other'
layer5.size_att = 'flux'
layer5.size_scale = 100
wwt.wait(2)
@ -403,16 +401,17 @@ def test_table_layers_cartesian_image(tmpdir, wwt_qt_client):
table['y'] = [0, 0.2, 0.4, 0.6, 0.8] * u.au
table['z'] = [0, 0.1, 0.2, 0.3, 0.4] * u.au
layer1 = wwt.layers.add_table_layer(table=table, coord_type='rectangular', size_scale=100, frame='Sky')
layer1 = wwt.layers.add_table_layer(table=table, coord_type='rectangular', size_scale=100, frame='Sky') # noqa
table = Table()
table['x'] = [1, 2, 3, 4, 5] * u.au
table['y'] = [-0.2, 0, 0.2, 0.4, 0.6 ] * u.au
table['y'] = [-0.2, 0, 0.2, 0.4, 0.6] * u.au
table['z'] = [0, 0.2, 0.4, 0.6, 0.8] * u.au
layer2 = wwt.layers.add_table_layer(table=table, coord_type='rectangular', frame='Sky')
layer2.cmap_att = 'x'
layer2.size_att = 'x'
layer2.size_scale = 100
wwt.wait(2)

Просмотреть файл

@ -1,7 +1,7 @@
import pytest
from ..core import BaseWWTWidget
from ..layers import SIZE_COLUMN_NAME, CMAP_COLUMN_NAME
from ..layers import SIZE_COLUMN_NAME, CMAP_COLUMN_NAME
import numpy as np
@ -18,8 +18,11 @@ STARDARD_WWT_SETTINGS = ['actualPlanetScale', 'showAltAzGrid', 'showConstellatio
'locationAltitude', 'locationLat', 'locationLng']
# Mock class so that we test serialization without instantiating an actual widget
class MockWWTWidget(BaseWWTWidget):
"""
Mock class so that we test serialization without instantiating an actual widget
"""
def quick_serialize(self):
return self._serialize_state(None, None, None)
@ -30,6 +33,7 @@ class MockWWTWidget(BaseWWTWidget):
def _serve_file(self, filename, extension=''):
return filename
def test_basic_serialization():
widget = MockWWTWidget()
test_state = widget._serialize_state('Title', 100, 200)
@ -62,6 +66,7 @@ def test_basic_serialization():
assert 'annotations' in test_state
assert test_state['annotations'] == []
def test_widget_settings_serialization():
widget = MockWWTWidget()
widget.actual_planet_scale = True
@ -71,9 +76,9 @@ def test_widget_settings_serialization():
widget.constellation_figure_color = '#24680b'
widget.constellation_figures = False
widget.constellation_selection = True
widget.constellation_selection_color = 'c' #cyan
widget.constellation_selection_color = 'c' # cyan
widget.crosshairs = True
widget.crosshairs_color = (128./255.,64./255.,16./255.)
widget.crosshairs_color = (128./255., 64./255., 16./255.)
widget.ecliptic = False
widget.ecliptic_grid = True
widget.galactic_grid = False
@ -83,36 +88,63 @@ def test_widget_settings_serialization():
widget.location_altitude = 7*u.m
widget.location_latitude = 12*u.deg
widget.location_longitude = -18*u.deg
expected_settings = {'actualPlanetScale': True, 'showAltAzGrid': False, 'showConstellationBoundries': True,
'constellationBoundryColor': '#ff0000', 'constellationFigureColor': '#24680b',
'showConstellationFigures': False, 'showConstellationSelection': True,
'constellationSelectionColor': '#00bfbf', 'showCrosshairs': True, 'crosshairsColor': '#804010',
'showEcliptic': False, 'showEclipticGrid': True, 'showGalacticGrid': False,
'galacticMode': True, 'showGrid': False, 'localHorizonMode': True, 'locationAltitude': 7,
'locationLat': 12., 'locationLng': -18.}
expected_settings = {'actualPlanetScale': True,
'showAltAzGrid': False,
'showConstellationBoundries': True,
'constellationBoundryColor': '#ff0000',
'constellationFigureColor': '#24680b',
'showConstellationFigures': False,
'showConstellationSelection': True,
'constellationSelectionColor': '#00bfbf',
'showCrosshairs': True,
'crosshairsColor': '#804010',
'showEcliptic': False,
'showEclipticGrid': True,
'showGalacticGrid': False,
'galacticMode': True,
'showGrid': False,
'localHorizonMode': True,
'locationAltitude': 7,
'locationLat': 12.,
'locationLng': -18.}
state = widget.quick_serialize()
assert state['wwt_settings'] == expected_settings
def test_mode_serialization():
view_mode_map = {'sky': 'sky', 'Sun': 'sun', 'Mercury': 'mercury', 'venus': 'venus', 'Earth': 'Bing Maps Aerial',
'moon': 'moon', 'mars': 'Visible Imagery', 'jupiter': 'jupiter', 'callisto': 'callisto',
'europa': 'europa', 'ganymede': 'ganymede', 'Io': 'io', 'saturn': 'saturn', 'Uranus': 'uranus',
'neptune': 'neptune', 'Pluto': 'pluto', 'panorama': 'panorama',
'Solar System': '3D Solar System View', 'milky way': '3D Solar System View',
view_mode_map = {'sky': 'sky',
'Sun': 'sun',
'Mercury': 'mercury',
'venus': 'venus',
'Earth': 'Bing Maps Aerial',
'moon': 'moon',
'mars': 'Visible Imagery',
'jupiter': 'jupiter',
'callisto': 'callisto',
'europa': 'europa',
'ganymede': 'ganymede',
'Io': 'io',
'saturn': 'saturn',
'Uranus': 'uranus',
'neptune': 'neptune',
'Pluto': 'pluto',
'panorama': 'panorama',
'Solar System': '3D Solar System View',
'milky way': '3D Solar System View',
'universe': '3D Solar System View'}
widget = MockWWTWidget()
for in_mode, out_mode in view_mode_map.items():
widget.set_view(in_mode)
assert widget.quick_serialize()['view_settings']['mode'] == out_mode, 'Mismatch for requested mode: {0}'.format(in_mode)
assert widget.quick_serialize()['view_settings']['mode'] == out_mode, \
'Mismatch for requested mode: {0}'.format(in_mode)
def test_3d_serialization():
widget = MockWWTWidget()
widget.set_view('milky way')
widget.solar_system.cosmos=True
widget.solar_system.lighting=False
widget.solar_system.cosmos = True
widget.solar_system.lighting = False
widget.solar_system.milky_way = True
widget.solar_system.minor_orbits = False
widget.solar_system.orbits = True
@ -120,10 +152,14 @@ def test_3d_serialization():
widget.solar_system.scale = 8
widget.solar_system.stars = True
expected_3d_settings = {'solarSystemCosmos':True, 'solarSystemLighting':False, 'solarSystemMilkyWay':True,
'solarSystemMinorOrbits':False, 'solarSystemOrbits':True, 'solarSystemPlanets':False,
expected_3d_settings = {'solarSystemCosmos': True,
'solarSystemLighting': False,
'solarSystemMilkyWay': True,
'solarSystemMinorOrbits': False,
'solarSystemOrbits': True,
'solarSystemPlanets': False,
'solarSystemScale': '8', # The validation method casts the int to a string
'solarSystemStars':True}
'solarSystemStars': True}
init_state = widget.quick_serialize()
settings = init_state['wwt_settings']
@ -135,13 +171,31 @@ def test_3d_serialization():
assert 'tracked_object_id' in init_state['view_settings']
assert init_state['view_settings']['tracked_object_id'] == 0
track_id_map = {'sun': 0, 'mercury': 1, 'venus': 2, 'mars': 3, 'jupiter': 4, 'saturn': 5, 'uranus': 6, 'neptune': 7,
'pluto': 8, 'moon': 9, 'io': 10, 'europa': 11, 'ganymede': 12, 'callisto': 13, 'ioshadow': 14,
'europashadow': 15, 'ganymedeshadow': 16, 'callistoshadow': 17, 'suneclipsed': 18, 'earth': 19}
track_id_map = {'sun': 0,
'mercury': 1,
'venus': 2,
'mars': 3,
'jupiter': 4,
'saturn': 5,
'uranus': 6,
'neptune': 7,
'pluto': 8,
'moon': 9,
'io': 10,
'europa': 11,
'ganymede': 12,
'callisto': 13,
'ioshadow': 14,
'europashadow': 15,
'ganymedeshadow': 16,
'callistoshadow': 17,
'suneclipsed': 18,
'earth': 19}
for obj_name, obj_id in track_id_map.items():
widget.solar_system.track_object(obj_name)
assert widget.quick_serialize()['view_settings']['tracked_object_id'] == obj_id, "ID mismatch for {0}".format(obj_name)
assert widget.quick_serialize()['view_settings']['tracked_object_id'] == obj_id, \
"ID mismatch for {0}".format(obj_name)
def test_add_remove_annotation_serialization():
@ -168,7 +222,7 @@ def test_add_remove_annotation_serialization():
def test_circle_annotation_serialization():
widget = MockWWTWidget()
circ = widget.add_circle(fill_color='#012345', radius=0.3*u.deg)
circ.set_center(SkyCoord(0.1*u.deg,0.2*u.deg))
circ.set_center(SkyCoord(0.1 * u.deg, 0.2 * u.deg))
circ.fill = True
circ.tag = 'Test Circ Tag'
circ.line_color = 'orange'
@ -176,8 +230,16 @@ def test_circle_annotation_serialization():
circ.opacity = 0.7
circ.label = 'Test Circ Label'
circ.hover_label = True
expected_settings = {'radius': 0.3, 'fill': True, 'tag': 'Test Circ Tag', 'fillColor': '#012345', 'lineColor': '#ffa500',
'lineWidth': 5, 'opacity': 0.7, 'label': 'Test Circ Label', 'showHoverLabel': True, 'skyRelative': True}
expected_settings = {'radius': 0.3,
'fill': True,
'tag': 'Test Circ Tag',
'fillColor': '#012345',
'lineColor': '#ffa500',
'lineWidth': 5,
'opacity': 0.7,
'label': 'Test Circ Label',
'showHoverLabel': True,
'skyRelative': True}
annot_state = widget.quick_serialize()['annotations'][0]
@ -199,16 +261,16 @@ def test_circle_annotation_serialization():
annot_state = widget.quick_serialize()['annotations'][0]
assert annot_state['settings'] == expected_settings
#Check circle annotation with no specified center
# Check circle annotation with no specified center
circ.remove()
circ2 = widget.add_circle()
center = widget.quick_serialize()['annotations'][0]['center']
assert center['ra'] == pytest.approx(5.)
assert center['dec'] == 10.
#Circle annotation with center in constructor
# Circle annotation with center in constructor
circ2.remove()
widget.add_circle(center = SkyCoord(15*u.deg,16*u.deg))
widget.add_circle(center=SkyCoord(15 * u.deg, 16 * u.deg))
center = widget.quick_serialize()['annotations'][0]['center']
assert center['ra'] == 15
assert center['dec'] == 16
@ -216,25 +278,31 @@ def test_circle_annotation_serialization():
def test_poly_annotation_setting():
widget = MockWWTWidget()
poly = widget.add_polygon(fill = True, tag='Test Poly Tag')
poly = widget.add_polygon(fill=True, tag='Test Poly Tag')
poly.fill_color = '#123456'
poly.line_color = 'antiquewhite'
poly.line_width = 9*u.pix
poly.opacity = 0.9
poly.label = 'Test Poly Label'
poly.hover_label = False
poly.add_point(SkyCoord([1,2,3]*u.deg,[5,6,7]*u.deg))
poly.add_point(SkyCoord(4*u.deg,8*u.deg))
expected_settings = {'fill': True, 'tag': 'Test Poly Tag', 'fillColor': '#123456', 'lineColor': '#faebd7',
'lineWidth': 9, 'opacity': 0.9, 'label': 'Test Poly Label', 'showHoverLabel': False}
poly.add_point(SkyCoord([1, 2, 3]*u.deg, [5, 6, 7]*u.deg))
poly.add_point(SkyCoord(4 * u.deg, 8 * u.deg))
expected_settings = {'fill': True,
'tag': 'Test Poly Tag',
'fillColor': '#123456',
'lineColor': '#faebd7',
'lineWidth': 9,
'opacity': 0.9,
'label': 'Test Poly Label',
'showHoverLabel': False}
annot_state = widget.quick_serialize()['annotations'][0]
assert annot_state['id'] == poly.id
assert annot_state['shape'] == 'polygon'
expected_ras = [1,2,3,4]
expected_decs = [5,6,7,8]
expected_ras = [1, 2, 3, 4]
expected_decs = [5, 6, 7, 8]
assert 'points' in annot_state
pts = annot_state['points']
assert len(pts) == 4
@ -245,26 +313,31 @@ def test_poly_annotation_setting():
assert 'settings' in annot_state
assert annot_state['settings'] == expected_settings
def test_line_annotation_setting():
widget = MockWWTWidget()
line = widget.add_line(color = '#abcde0')
line = widget.add_line(color='#abcde0')
line.tag = 'Test Line Tag'
line.width = 11*u.pix
line.opacity = 0.2
line.label = 'Test Line Label'
line.hover_label = True
line.add_point(SkyCoord([2,4,6]*u.deg,[10,12,14]*u.deg))
line.add_point(SkyCoord(8*u.deg,16*u.deg))
expected_settings = {'tag': 'Test Line Tag', 'lineColor': '#abcde0', 'lineWidth': 11, 'opacity': 0.2,
'label': 'Test Line Label', 'showHoverLabel': True}
line.add_point(SkyCoord([2, 4, 6] * u.deg, [10, 12, 14] * u.deg))
line.add_point(SkyCoord(8 * u.deg, 16 * u.deg))
expected_settings = {'tag': 'Test Line Tag',
'lineColor': '#abcde0',
'lineWidth': 11,
'opacity': 0.2,
'label': 'Test Line Label',
'showHoverLabel': True}
annot_state = widget.quick_serialize()['annotations'][0]
assert annot_state['id'] == line.id
assert annot_state['shape'] == 'line'
expected_ras = [2,4,6,8]
expected_decs = [10,12,14,16]
expected_ras = [2, 4, 6, 8]
expected_decs = [10, 12, 14, 16]
assert 'points' in annot_state
pts = annot_state['points']
assert len(pts) == 4
@ -309,7 +382,7 @@ def test_add_remove_layer_serialization():
widget.layers.remove_layer(img2)
state = widget.quick_serialize()
layer_ids = [table1.id,img1.id]
layer_ids = [table1.id, img1.id]
assert len(state['layers']) == 2
for layer in state['layers']:
assert layer['id'] in layer_ids
@ -319,6 +392,7 @@ def test_add_remove_layer_serialization():
state = widget.quick_serialize()
assert len(state['layers']) == 0
def test_table_setting_serialization():
widget = MockWWTWidget()
@ -341,21 +415,31 @@ def test_table_setting_serialization():
assert layer_state['frame'] == 'Earth'
assert 'settings' in layer_state
expected_settings = {'lngColumn':'ra', 'raUnits': 'degrees',
'latColumn': 'dec', 'altColumn': 'flux',
'timeSeries': False, 'decay': 16 * u.day,
'altUnit': None, 'altType': 'distance',
'color': '#aacc00', 'scaleFactor': 14,
'opacity': 0.75, 'plotType': 'square',
'markerScale': 'world', 'showFarSide': True,
'sizeColumn': -1, '_colorMap': 0,
'colorMapColumn': -1, 'xAxisColumn': '',
'yAxisColumn': '', 'zAxisColumn': '',
expected_settings = {'lngColumn': 'ra',
'raUnits': 'degrees',
'latColumn': 'dec',
'altColumn': 'flux',
'timeSeries': False,
'decay': 16 * u.day,
'altUnit': None,
'altType': 'distance',
'color': '#aacc00',
'scaleFactor': 14,
'opacity': 0.75,
'plotType': 'square',
'markerScale': 'world',
'showFarSide': True,
'sizeColumn': -1,
'_colorMap': 0,
'colorMapColumn': -1,
'xAxisColumn': '',
'yAxisColumn': '',
'zAxisColumn': '',
'cartesianScale': None,
'coordinatesType': 'spherical'}
assert layer_state['settings'] == expected_settings
#Check when we have colormap and scaling
# Check when we have colormap and scaling
layer.cmap_att = 'flux'
layer.size_att = 'dec'
layer.alt_unit = u.Mpc
@ -368,6 +452,7 @@ def test_table_setting_serialization():
assert widget.quick_serialize()['layers'][0]['settings'] == expected_settings
def test_image_setting_serialization():
widget = MockWWTWidget()
@ -400,4 +485,5 @@ def test_image_setting_serialization():
stretches = {'linear': 0, 'log': 1, 'power': 2, 'sqrt': 3, 'histeq': 4}
for stretch_name, stretch_id in stretches.items():
layer.stretch = stretch_name
assert widget.quick_serialize()['layers'][0]['stretch_info']['stretch'] == stretch_id, "Stretch id mismatch for: {0}".format(stretch_name)
assert widget.quick_serialize()['layers'][0]['stretch_info']['stretch'] == stretch_id, \
"Stretch id mismatch for: {0}".format(stretch_name)

Просмотреть файл

@ -5,7 +5,6 @@ from traitlets import (TraitType, TraitError,
Int as OriginalInt,
Unicode as OriginalUnicode)
from astropy import units as u
import six
try:
from matplotlib.colors import to_hex
@ -89,8 +88,8 @@ class Color(TraitType):
self.from_cwo = False
def validate(self, obj, value):
if (isinstance(value, six.string_types)
or (isinstance(value, tuple) and len(value) == 3)):
if (isinstance(value, str) or
(isinstance(value, tuple) and len(value) == 3)):
return to_hex(value)
else:
if self.from_cwo:

Просмотреть файл

@ -43,6 +43,7 @@ def validate_traits(cls, traits):
mismatch,
'' if len(mismatch) > 1 else 'es'))
def ensure_utc(tm, str_allowed):
'''
Helper function to convert a time object (Time, datetime, or UTC string
@ -65,7 +66,7 @@ def ensure_utc(tm, str_allowed):
utc_tm = tm.to_datetime(pytz.UTC).isoformat()
else:
if str_allowed: # is an ISOT string
if str_allowed: # is an ISOT string
dt = Time(tm, format='isot').to_datetime(pytz.UTC)
utc_tm = dt.isoformat()
else:

Просмотреть файл

@ -99,10 +99,11 @@ setup_args = dict(
'Framework :: Jupyter',
],
include_package_data = True,
python_requires = '>=3.6',
install_requires = [
'numpy>=1.9',
'matplotlib>1.5',
'astropy>=1.0',
'astropy>=1.0,!=4.0.1',
'requests',
'beautifulsoup4',
'python-dateutil',
@ -112,9 +113,7 @@ setup_args = dict(
'traitlets',
'reproject>=0.4',
'qtpy',
'flask',
'flask-cors',
'six',
'tornado',
'pytz',
],
extras_require = {

35
tox.ini Normal file
Просмотреть файл

@ -0,0 +1,35 @@
[tox]
envlist = py{36,37,38}-{test,docs}
requires = pip >= 18.0
setuptools >= 30.3.0
[testenv]
passenv =
DISPLAY
HOME
setenv =
CI = true
changedir =
docs: docs
deps:
PyQt5==5.9.*
test: pyopengl
extras =
test: test,lab
docs: docs
commands =
test: pip freeze
test: python -c 'import pywwt'
test: jupyter nbextension list
test: jupyter labextension list
test: jupyter serverextension list
test: python .check_enabled.py
test: pytest pywwt --cov pywwt -p no:warnings {posargs}
docs: sphinx-build -n -b html -d _build/doctrees . _build/html
docs: sphinx-build -n -b linkcheck -d _build/doctrees . _build/html
[testenv:codestyle]
deps = flake8
skip_install = true
commands =
flake8 --max-line-length=200 pywwt