diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 1f8750e8c..000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,25 +0,0 @@ -environment: - global: - CHANS_DEV: "-c pyviz/label/dev" - CHANS_REL: "-c pyviz" - matrix: - - PY: "3.6" - CONDA: "C:\\Miniconda36-x64" - -install: - - "SET PATH=%CONDA%;%CONDA%\\Scripts;%PATH%" - - "conda install -y -c pyviz pyctdev && doit ecosystem_setup" - - "doit env_create %CHANS_REL% --name=test --python=%PY%" - - "activate test" - - "doit develop_install %CHANS_REL% -o examples -o tests" - - "doit env_capture" - -build: off -skip_branch_with_pr: true # only do one run per commit -clone_depth: 5 -skip_tags: true - -test_script: - - "bokeh sampledata" - - "doit test_flakes" - - "doit test_examples" diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 000000000..e7b173cba --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,109 @@ +name: tests +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +jobs: + test_suite: + name: Pytest on ${{ matrix.os }} with Python ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ['ubuntu-latest', 'windows-latest'] + python-version: [2.7, 3.6, 3.7] + exclude: + - os: windows-latest + python-version: 2.7 + timeout-minutes: 30 + defaults: + run: + shell: bash -l {0} + env: + DESC: "Python ${{ matrix.python-version }} tests" + HV_REQUIREMENTS: "unit_tests" + PYTHON_VERSION: ${{ matrix.python-version }} + CHANS_DEV: "-c pyviz/label/dev" + CHANS: "-c pyviz" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: "100" + - uses: actions/setup-python@v2 + with: + auto-update-conda: true + python-version: ${{ matrix.python-version }} + - uses: conda-incubator/setup-miniconda@v2 + with: + miniconda-version: "latest" + - name: Fetch unshallow + run: git fetch --prune --tags --unshallow + - name: conda setup + run: | + conda config --set always_yes True + conda install -c pyviz "pyctdev>=0.5" + doit ecosystem_setup + conda install nodejs + doit env_create ${{ env.CHANS_DEV}} --python=${{ matrix.python-version }} + - name: doit develop_install py2 + if: startsWith(matrix.python-version, 2.) + run: | + eval "$(conda shell.bash hook)" + conda activate test-environment + conda list + doit develop_install -c pyviz/label/dev -o tests + - name: doit develop_install py3 + if: startsWith(matrix.python-version, 3.) + run: | + eval "$(conda shell.bash hook)" + conda activate test-environment + conda list + doit develop_install ${{ env.CHANS_DEV}} -o examples -o tests + - name: pygraphviz + if: contains(matrix.os, 'ubuntu') + run: | + eval "$(conda shell.bash hook)" + conda activate test-environment + conda install -c conda-forge pygraphviz + - name: doit env_capture + run: | + eval "$(conda shell.bash hook)" + conda activate test-environment + doit env_capture + - name: doit test_flakes + run: | + eval "$(conda shell.bash hook)" + conda activate test-environment + doit test_flakes + - name: doit test_unit + run: | + eval "$(conda shell.bash hook)" + conda activate test-environment + doit test_unit + - name: test examples ubuntu + if: contains(matrix.os, 'ubuntu') && startsWith(matrix.python-version, 3.) + run: | + eval "$(conda shell.bash hook)" + conda activate test-environment + bokeh sampledata + doit test_examples_extra + - name: test examples windows + if: contains(matrix.os, 'windows') + run: | + eval "$(conda shell.bash hook)" + conda activate test-environment + bokeh sampledata + doit test_examples + - name: codecov + env: + github-token: ${{ secrets.GITHUB_TOKEN }} + if: startsWith(matrix.python-version, 3.) + run: | + eval "$(conda shell.bash hook)" + conda activate test-environment + coveralls diff --git a/.travis.yml b/.travis.yml index 9f80238aa..54cdb8e04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,8 @@ env: - PYTHON_VERSION=3.6 stages: - - test + - name: test + if: tag =~ ^v(\d+|\.)+([a-z]|rc)?\d?$ - name: docs if: tag =~ ^v(\d+|\.)*[^a-z]\d*$ OR tag = website - name: docs_dev diff --git a/MANIFEST.in b/MANIFEST.in index 379c5f543..d3f0d05e7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ -include hvplot/.version +include README.rst include LICENSE +include hvplot/.version graft examples global-exclude *.py[co] global-exclude *~ diff --git a/examples/user_guide/NetworkX.ipynb b/examples/user_guide/NetworkX.ipynb index 99a369604..a69083891 100644 --- a/examples/user_guide/NetworkX.ipynb +++ b/examples/user_guide/NetworkX.ipynb @@ -600,8 +600,8 @@ "G.add_edge('c', 'f', weight=0.9)\n", "G.add_edge('a', 'd', weight=0.3)\n", "\n", - "elarge = [(u, v) for (u, v, d) in G.edges(data=True) if d['weight'] > 0.5]\n", - "esmall = [(u, v) for (u, v, d) in G.edges(data=True) if d['weight'] <= 0.5]\n", + "elarge = [(u, v) for (u, v, attr) in G.edges(data=True) if attr['weight'] > 0.5]\n", + "esmall = [(u, v) for (u, v, attr) in G.edges(data=True) if attr['weight'] <= 0.5]\n", "\n", "pos = nx.spring_layout(G) # positions for all nodes\n", "\n", diff --git a/hvplot/converter.py b/hvplot/converter.py index f75cde8b4..876ce0d72 100644 --- a/hvplot/converter.py +++ b/hvplot/converter.py @@ -1042,7 +1042,10 @@ def method_wrapper(ds, x, y): name = data.name or self.label or self.value_label dataset = Dataset(data, self.indexes, name) else: - dataset = Dataset(data, self.indexes) + try: + dataset = Dataset(data, self.indexes) + except Exception: + dataset = Dataset(data) dataset = dataset.redim(**self._redim) obj = method(x, y) obj._dataset = dataset diff --git a/hvplot/intake.py b/hvplot/intake.py index 0628272f8..05a1f2ab8 100644 --- a/hvplot/intake.py +++ b/hvplot/intake.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from distutils.version import LooseVersion from . import hvPlot, post_patch diff --git a/hvplot/tests/testcharts.py b/hvplot/tests/testcharts.py index 69481c26e..826995714 100644 --- a/hvplot/tests/testcharts.py +++ b/hvplot/tests/testcharts.py @@ -9,6 +9,7 @@ class TestChart2D(ComparisonTestCase): + def setUp(self): try: import numpy as np @@ -76,7 +77,7 @@ def test_heatmap_2d_derived_x_and_y(self): class TestChart2DDask(TestChart2D): def setUp(self): - super().setUp() + super(TestChart2DDask, self).setUp() try: import dask.dataframe as dd except: @@ -250,27 +251,27 @@ def test_time_df_sorts_on_plot(self): scrambled = self.time_df.sample(frac=1) plot = scrambled.hvplot(x='time') assert (plot.data == self.time_df).all().all() - assert (plot.data.time.diff()[1:].astype('int') > 0).all() + assert len(plot.data.time.unique()) == len(plot.data.time) def test_time_df_does_not_sort_on_plot_if_sort_date_off(self): scrambled = self.time_df.sample(frac=1) plot = scrambled.hvplot(x='time', sort_date=False) assert (plot.data == scrambled).all().all() - assert not (plot.data.time.diff()[1:].astype('int') > 0).all() + assert len(plot.data.time.unique()) == len(plot.data.time) def test_time_df_sorts_on_plot_using_index_as_x(self): df = self.time_df.set_index('time') scrambled = df.sample(frac=1) plot = scrambled.hvplot() assert (plot.data['time'] == df.index).all() - assert (plot.data.time.diff()[1:].astype('int') > 0).all() + assert len(plot.data.time.unique()) == len(plot.data.time) def test_time_df_does_not_sort_on_plot_if_sort_date_off_using_index_as_x(self): df = self.time_df.set_index('time') scrambled = df.sample(frac=1) plot = scrambled.hvplot(sort_date=False) assert (plot.data.time == scrambled.index).all().all() - assert not (plot.data.time.diff()[1:].astype('int') > 0).all() + assert len(plot.data.time.unique()) == len(plot.data.time) def test_time_df_with_groupby_as_derived_datetime(self): plot = self.time_df.hvplot(groupby='time.dayofweek', dynamic=False) @@ -306,7 +307,7 @@ def test_default_y_not_in_by(self): class TestChart1DDask(TestChart1D): def setUp(self): - super().setUp() + super(TestChart1DDask, self).setUp() try: import dask.dataframe as dd except: diff --git a/hvplot/tests/testgeo.py b/hvplot/tests/testgeo.py index 25dab23b1..58177c17d 100644 --- a/hvplot/tests/testgeo.py +++ b/hvplot/tests/testgeo.py @@ -1,3 +1,5 @@ +import sys + from unittest import TestCase, SkipTest, expectedFailure import numpy as np @@ -8,6 +10,8 @@ class TestGeo(TestCase): def setUp(self): + if sys.platform == "win32": + raise SkipTest("Skip geo tests on windows for now") try: import xarray as xr import rasterio # noqa @@ -29,10 +33,14 @@ def assert_projection(self, plot, proj): opts = hv.Store.lookup_options('bokeh', plot, 'plot') assert opts.kwargs['projection'].proj4_params['proj'] == proj - def test_plot_with_crs_as_object(self): - plot = self.da.hvplot.image('x', 'y', crs=self.crs) - self.assertCRS(plot) +class TestCRSInference(TestGeo): + + def setUp(self): + if sys.platform == "win32": + raise SkipTest("Skip CRS inference on Windows") + super(TestCRSInference, self).setUp() + def test_plot_with_crs_as_proj_string(self): plot = self.da.hvplot.image('x', 'y', crs=self.da.crs) self.assertCRS(plot) @@ -41,6 +49,13 @@ def test_plot_with_geo_as_true_crs_undefined(self): plot = self.da.hvplot.image('x', 'y', geo=True) self.assertCRS(plot) + +class TestProjections(TestGeo): + + def test_plot_with_crs_as_object(self): + plot = self.da.hvplot.image('x', 'y', crs=self.crs) + self.assertCRS(plot) + def test_plot_with_crs_as_attr_str(self): da = self.da.copy() da.attrs = {'bar': self.crs} @@ -59,12 +74,12 @@ def test_plot_with_geo_as_true_crs_no_crs_on_data_returns_default(self): def test_plot_with_projection_as_string(self): da = self.da.copy() - plot = da.hvplot.image('x', 'y', projection='Robinson') + plot = da.hvplot.image('x', 'y', crs=self.crs, projection='Robinson') self.assert_projection(plot, 'robin') def test_plot_with_projection_as_string_google_mercator(self): da = self.da.copy() - plot = da.hvplot.image('x', 'y', projection='GOOGLE_MERCATOR') + plot = da.hvplot.image('x', 'y', crs=self.crs, projection='GOOGLE_MERCATOR') self.assert_projection(plot, 'merc') def test_plot_with_projection_as_invalid_string(self): diff --git a/hvplot/tests/testnetworkx.py b/hvplot/tests/testnetworkx.py index c289e8204..e6756c17b 100644 --- a/hvplot/tests/testnetworkx.py +++ b/hvplot/tests/testnetworkx.py @@ -12,7 +12,7 @@ class TestOptions(TestCase): def setUp(self): # Create nodes (1-10) in unsorted order nodes = np.array([1, 4, 5, 10, 8, 9, 3, 7, 2, 6]) - edges = [*zip(nodes[:-1], nodes[1:])] + edges = list(zip(nodes[:-1], nodes[1:])) g = nx.Graph() g.add_nodes_from(nodes) diff --git a/hvplot/tests/testtransforms.py b/hvplot/tests/testtransforms.py index ef5f1a36e..82121e045 100644 --- a/hvplot/tests/testtransforms.py +++ b/hvplot/tests/testtransforms.py @@ -25,13 +25,13 @@ def test_pandas_transform(self): class TestXArrayTransforms(ComparisonTestCase): def setUp(self): - + try: - import xarray as xr + import xarray as xr # noqa except: raise SkipTest('xarray not available') import hvplot.xarray # noqa - + def test_xarray_transform(self): import xarray as xr data = np.arange(0, 60).reshape(6, 10) @@ -42,6 +42,3 @@ def test_xarray_transform(self): transforms=dict(value=hv.dim('value')*10) ) self.assertEqual(img.data.value.data, da.data*10) - - - diff --git a/hvplot/tests/testutil.py b/hvplot/tests/testutil.py index f1adb1ca0..a4070445b 100644 --- a/hvplot/tests/testutil.py +++ b/hvplot/tests/testutil.py @@ -1,6 +1,8 @@ """ Tests utilities to convert data and projections """ +import sys + import numpy as np from unittest import TestCase, SkipTest @@ -195,6 +197,8 @@ def test_process_xarray_dataset_with_x_as_derived_datetime(self): class TestGeoUtil(TestCase): def setUp(self): + if sys.platform == "win32": + raise SkipTest("Skip geo tests on windows for now") try: import geoviews # noqa import cartopy.crs as ccrs diff --git a/setup.py b/setup.py index a91918fbe..5c4eb7107 100644 --- a/setup.py +++ b/setup.py @@ -1,123 +1,29 @@ +import json import os import sys import shutil -from collections import defaultdict -from setuptools import setup, find_packages - -########## autover ########## +from setuptools import setup, find_packages -def embed_version(basepath, ref='v0.2.2'): - """ - Autover is purely a build time dependency in all cases (conda and - pip) except for when you use pip's remote git support [git+url] as - 1) you need a dynamically changing version and 2) the environment - starts off clean with zero dependencies installed. - This function acts as a fallback to make Version available until - PEP518 is commonly supported by pip to express build dependencies. - """ - import io, zipfile, importlib - try: from urllib.request import urlopen - except: from urllib import urlopen - try: - url = 'https://github.com/ioam/autover/archive/{ref}.zip' - response = urlopen(url.format(ref=ref)) - zf = zipfile.ZipFile(io.BytesIO(response.read())) - ref = ref[1:] if ref.startswith('v') else ref - embed_version = zf.read('autover-{ref}/autover/version.py'.format(ref=ref)) - with open(os.path.join(basepath, 'version.py'), 'wb') as f: - f.write(embed_version) - return importlib.import_module("version") - except: - return None +import pyct.build def get_setup_version(reponame): """ Helper to get the current version from either git describe or the .version file (if available). """ - import json basepath = os.path.split(__file__)[0] version_file_path = os.path.join(basepath, reponame, '.version') try: from param import version except: - version = embed_version(basepath) + version = None if version is not None: return version.Version.setup_version(basepath, reponame, archive_commit="$Format:%h$") else: print("WARNING: param>=1.6.0 unavailable. If you are installing a package, this warning can safely be ignored. If you are creating a package or otherwise operating in a git repository, you should install param>=1.6.0.") return json.load(open(version_file_path, 'r'))['version_string'] -########## examples ########## - -def check_pseudo_package(path): - """ - Verifies that a fake subpackage path for assets (notebooks, svgs, - pngs etc) both exists and is populated with files. - """ - if not os.path.isdir(path): - raise Exception("Please make sure pseudo-package %s exists." % path) - else: - assets = os.listdir(path) - if len(assets) == 0: - raise Exception("Please make sure pseudo-package %s is populated." % path) - - -excludes = ['DS_Store', '.log', 'ipynb_checkpoints'] -packages = [] -extensions = defaultdict(list) - -def walker(top, names): - """ - Walks a directory and records all packages and file extensions. - """ - global packages, extensions - if any(exc in top for exc in excludes): - return - package = top[top.rfind('hvplot'):].replace(os.path.sep, '.') - packages.append(package) - for name in names: - ext = '.'.join(name.split('.')[1:]) - ext_str = '*.%s' % ext - if ext and ext not in excludes and ext_str not in extensions[package]: - extensions[package].append(ext_str) - - -def examples(path='hvplot-examples', verbose=False, force=False, root=__file__): - """ - Copies the notebooks to the supplied path. - """ - filepath = os.path.abspath(os.path.dirname(root)) - example_dir = os.path.join(filepath, './examples') - if not os.path.exists(example_dir): - example_dir = os.path.join(filepath, '../examples') - if os.path.exists(path): - if not force: - print('%s directory already exists, either delete it or set the force flag' % path) - return - shutil.rmtree(path) - ignore = shutil.ignore_patterns('.ipynb_checkpoints', '*.pyc', '*~') - tree_root = os.path.abspath(example_dir) - if os.path.isdir(tree_root): - shutil.copytree(tree_root, path, ignore=ignore, symlinks=True) - else: - print('Cannot find %s' % tree_root) - - - -def package_assets(example_path): - """ - Generates pseudo-packages for the examples directory. - """ - examples(example_path, force=True, root=__file__) - for root, dirs, files in os.walk(example_path): - walker(root, dirs+files) - setup_args['packages'] += packages - for p, exts in extensions.items(): - if exts: - setup_args['package_data'][p] = exts - ########## dependencies ########## @@ -149,7 +55,8 @@ def package_assets(example_path): 'selenium', 'spatialpandas', 'scikit-image', - 'python-snappy' + 'python-snappy', + 'pyepsg' ] _examples_extra = _examples + [ @@ -195,7 +102,7 @@ def package_assets(example_path): author_email= "developers@pyviz.org", maintainer="HoloViz developers", maintainer_email="developers@pyviz.org", - packages=find_packages()+packages, + packages=find_packages(), package_data={'hvplot': ['.version']}, platforms=['Windows', 'Mac OS X', 'Linux'], license='BSD', @@ -229,8 +136,9 @@ def package_assets(example_path): if __name__ == '__main__': example_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'hvplot','examples') - if 'develop' not in sys.argv: - package_assets(example_path) + + if 'develop' not in sys.argv and 'egg_info' not in sys.argv: + pyct.build.examples(example_path, __file__, force=True) setup(**setup_args) diff --git a/tox.ini b/tox.ini index 697778679..a077f8f85 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ [tox] # python version test group extra envs extra commands -envlist = {py27,py36,py37}-{flakes,unit,examples,examples_extra,all}-{default}-{dev,pkg} +envlist = {py27,py36,py37,py38}-{flakes,unit,examples,examples_extra,all}-{default}-{dev,pkg} [_flakes] description = Flake check python and notebooks @@ -63,7 +63,7 @@ deps = unit: {[_unit]deps} [pytest] addopts = -v --pyargs --doctest-modules --doctest-ignore-import-errors norecursedirs = doc .git dist build _build .ipynb_checkpoints -nbsmoke_skip_run = ^.*user_guide/NetworkX\.ipynb$ +nbsmoke_skip_run = ^.*NetworkX\.ipynb$ [flake8] include = *.py