Skip to content

Commit

Permalink
Merge pull request #4 from nens/casper-travis
Browse files Browse the repository at this point in the history
[Done] Travis + version compatibiltity fixes
  • Loading branch information
caspervdw committed Sep 4, 2019
2 parents 4e7121a + 83612d7 commit ea58d10
Show file tree
Hide file tree
Showing 17 changed files with 155 additions and 79 deletions.
42 changes: 42 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
language: minimal

matrix:
include:
- os: linux
env: PYTHON_VERSION="3.5" DEPS="numpy=1.12 gdal=2.0 dask-core=0.18 toolz=0.9 pandas=0.19 shapely=1.5 geopandas=0.4.0 pytz scipy=0.19"
- os: linux
env: PYTHON_VERSION="3.6" DEPS="numpy=1.14 gdal=2.2 dask-core=0.20 toolz=0.10 pandas=0.23 shapely=1.6 geopandas=0.4 pytz scipy=1.1"
- os: linux
env: PYTHON_VERSION="3.7" DEPS="numpy gdal dask-core toolz geopandas pytz scipy"
- os: osx
env: PYTHON_VERSION="3.7" DEPS="numpy gdal dask-core toolz geopandas pytz scipy"

before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh;
fi;

install:
# install miniconda
# -b means "batch mode": automatically agree with license
# -p means "prefix": determine installation prefix
- bash miniconda.sh -b -p $HOME/miniconda
# setup the paths and reset hash
- export PATH="$HOME/miniconda:$HOME/miniconda/bin:$PATH"
- source $HOME/miniconda/etc/profile.d/conda.sh
- hash -r
# update and configure miniconda
- conda update --yes conda
- conda config --set changeps1 no --set restore_free_channel true
- conda config --append channels conda-forge
# create and activate the test environment
- conda create -n testenv --yes python=$PYTHON_VERSION $DEPS pytest
- conda activate testenv
- pip install . --no-deps
# print stuff for debugging
- conda --version ; python --version ; pip --version;

script:
- pytest
13 changes: 13 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ Changelog of dask-geomodeling

- Clean up the .check() method for RasterBlocks.

- Added a Travisfile testing with against versions since 2017 on Linux and OSX.

- Took some python 3.5 compatibility measures.

- Added fix in ParseText block for pandas 0.23.

- Changed underscores in config to dashes for dask 0.18 compatibility.

- Constrained dask to >= 0.18, numpy to >= 1.12, pandas to >= 0.19,
geopandas to >= 0.4, scipy to >= 0.19.

- Removed the explicit (py)gdal dependency.


2.0.1 (2019-08-30)
------------------
Expand Down
4 changes: 2 additions & 2 deletions dask_geomodeling/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

defaults = {
"root": "/",
"raster_limit": 12 * (1024 ** 2), # ca. 100 MB of float64
"geometry_limit": 10000,
"raster-limit": 12 * (1024 ** 2), # ca. 100 MB of float64
"geometry-limit": 10000,
}

dask.config.update_defaults({"geomodeling": defaults})
11 changes: 6 additions & 5 deletions dask_geomodeling/core/graphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ def _construct_exc_callback(e, dumps):
"""
key = inspect.currentframe().f_back.f_locals.get("key")
e.args = (f"{key}: {str(e)}",)
raise
e.args = ("{0}: {1}".format(key, str(e)),)
raise e


def _reconstruct_token(key):
Expand Down Expand Up @@ -61,16 +61,17 @@ def construct_multiple(graph, names, validate=True):
if isinstance(cls, str):
cls = Block.from_import_path(cls)
if not issubclass(cls, Block):
raise TypeError(f'Cannot construct from object of type "{cls}"')
raise TypeError("Cannot construct from object of type '{}'".format(cls))
args = tuple(value[1:])
if validate:
new_graph[key] = (cls,) + args
else:
token = _reconstruct_token(key)
if token is None:
logger.warning(
f"Construct received a key with an invalid name ('{key}'),"
f"while validation was turned off"
"Construct received a key with an invalid name ('%s'),"
"while validation was turned off",
key,
)
new_graph[key] = (cls._init_no_validation, token) + args

Expand Down
14 changes: 7 additions & 7 deletions dask_geomodeling/geometry/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def __init__(
max_pixels=None,
column_name="agg",
auto_pixel_size=False,
*args,
*args
):
if not isinstance(source, GeometryBlock):
raise TypeError("'{}' object is not allowed".format(type(source)))
Expand Down Expand Up @@ -222,7 +222,7 @@ def __init__(
max_pixels,
column_name,
auto_pixel_size,
*args,
*args
)

@property
Expand Down Expand Up @@ -289,16 +289,16 @@ def get_sources_and_requests(self, **request):
# in case this request is too large, we adapt pixel size
max_pixels = self.max_pixels
if max_pixels is None:
max_pixels = config.get("geomodeling.raster_limit")
max_pixels = config.get("geomodeling.raster-limit")
pixel_size = self.pixel_size

if required_pixels > max_pixels and self.auto_pixel_size:
# adapt with integer multiples of pixel_size
pixel_size *= ceil(sqrt(required_pixels / max_pixels))
elif required_pixels > max_pixels:
raise RuntimeError(
f"The required raster size for the aggregation exceeded "
f"the maximum ({required_pixels} > {max_pixels})"
"The required raster size for the aggregation exceeded "
"the maximum ({} > {})".format(required_pixels, max_pixels)
)

# snap the extent to (0, 0) to prevent subpixel shifts
Expand Down Expand Up @@ -502,9 +502,9 @@ def __init__(
threshold_name=None,
):
if not isinstance(threshold_name, str):
raise TypeError(f"'{type(threshold_name)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(threshold_name)))
if threshold_name not in source.columns:
raise KeyError(f"Column '{threshold_name}' is not available")
raise KeyError("Column '{}' is not available".format(threshold_name))
super().__init__(
source,
raster,
Expand Down
2 changes: 1 addition & 1 deletion dask_geomodeling/geometry/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def __init__(self, source, name):
if not isinstance(name, str):
raise TypeError("'{}' object is not allowed".format(type(name)))
if name not in source.columns:
raise KeyError(f"Column '{name}' is not available")
raise KeyError("Column '{}' is not available".format(name))
super().__init__(source, name)

@property
Expand Down
32 changes: 17 additions & 15 deletions dask_geomodeling/geometry/field_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,21 @@ class Classify(BaseSingleSeries):

def __init__(self, source, bins, labels, right=True):
if not isinstance(bins, list):
raise TypeError(f"'{type(bins)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(bins)))
if not isinstance(labels, list):
raise TypeError(f"'{type(labels)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(labels)))
if not isinstance(right, bool):
raise TypeError(f"'{type(right)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(right)))
bins_arr = np.asarray(bins)
if bins_arr.ndim != 1:
raise TypeError("'bins' must be one-dimensional")
if (np.diff(bins) < 0).any():
raise ValueError("'bins' must increase monotonically.")
if len(labels) not in (len(bins) - 1, len(bins) + 1):
raise ValueError(
f"Expected {len(bins) - 1} or {len(bins) + 1} labels,"
f"got {len(labels)}"
"Expected {} or {} labels, got {}".format(
len(bins) - 1, len(bins) + 1, len(labels)
)
)
super().__init__(source, bins, labels, right)

Expand Down Expand Up @@ -127,22 +128,23 @@ class ClassifyFromColumns(SeriesBlock):

def __init__(self, source, value_column, bin_columns, labels, right=True):
if not isinstance(source, GeometryBlock):
raise TypeError(f"'{type(source)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(source)))
if not isinstance(value_column, str):
raise TypeError(f"'{type(value_column)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(value_column)))
if not isinstance(bin_columns, list):
raise TypeError(f"'{type(bin_columns)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(bin_columns)))
if not isinstance(labels, list):
raise TypeError(f"'{type(labels)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(labels)))
if not isinstance(right, bool):
raise TypeError(f"'{type(right)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(right)))
missing_columns = (set(bin_columns) | {value_column}) - source.columns
if missing_columns:
raise KeyError(f"Columns '{missing_columns}' are not present")
raise KeyError("Columns '{}' are not present".format(missing_columns))
if len(labels) not in (len(bin_columns) - 1, len(bin_columns) + 1):
raise ValueError(
f"Expected {len(bin_columns) - 1} or "
f"{len(bin_columns) + 1} labels, got {len(labels)}"
"Expected {} or {} labels, got {}".format(
len(bin_columns) - 1, len(bin_columns) + 1, len(labels)
)
)
super().__init__(source, value_column, bin_columns, labels, right)

Expand Down Expand Up @@ -539,7 +541,7 @@ class Round(BaseSingleSeries):

def __init__(self, source, decimals=0):
if not isinstance(decimals, int):
raise TypeError(f"'{type(decimals)}' object is not allowed")
raise TypeError("'{}' object is not allowed".format(type(decimals)))
super().__init__(source, decimals)

process = staticmethod(np.around)
Expand Down Expand Up @@ -575,7 +577,7 @@ def __init__(self, source, xp, fp, left=None, right=None):
if right is not None:
right = float(right)
if np.any(np.diff(xp) < 0):
raise ValueError(f"xp must be monotonically increasing")
raise ValueError("xp must be monotonically increasing")
super().__init__(source, xp, fp, left, right)

@staticmethod
Expand Down
2 changes: 1 addition & 1 deletion dask_geomodeling/geometry/parallelize.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def get_sources_and_requests(self, **request):
if mode == "extent":
return [(self.source, request)]
if mode != "centroid":
raise NotImplementedError(f"Cannot process '{mode}' mode")
raise NotImplementedError("Cannot process '{}' mode".format(mode))

# tile the requested geometry in boxes that have a maximum size
req_geometry = request["geometry"]
Expand Down
6 changes: 3 additions & 3 deletions dask_geomodeling/geometry/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,11 @@ def process(url, request):
if request.get("limit") and len(f) > request["limit"]:
f = f.iloc[: request["limit"]]
elif request.get("limit") is None:
global_limit = config.get("geomodeling.geometry_limit")
global_limit = config.get("geomodeling.geometry-limit")
if len(f) > global_limit:
raise RuntimeError(
f"The amount of returned geometries exceeded "
f"the maximum of {global_limit} geometries."
"The amount of returned geometries exceeded "
"the maximum of {} geometries.".format(global_limit)
)

return {"projection": request["projection"], "features": f}
13 changes: 9 additions & 4 deletions dask_geomodeling/geometry/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ class ParseTextColumn(BaseSingle):

def __init__(self, source, source_column, key_mapping):
if not isinstance(source, GeometryBlock):
raise TypeError(f"'{type(source)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(source)))
if not isinstance(source_column, str):
raise TypeError(f"'{type(source_column)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(source_column)))
if source_column not in source.columns:
raise KeyError(f"Column '{source_column}' is not available.")
raise KeyError("Column '{}' is not available.".format(source_column))
if not isinstance(key_mapping, dict):
raise TypeError(f"'{type(key_mapping)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(key_mapping)))
super().__init__(source, source_column, key_mapping)

@property
Expand Down Expand Up @@ -98,6 +98,11 @@ def process(data, kwargs):
# Hopefully, this is already the case:
column = f[source_column].astype("category")

if len(column.cat.categories) == 0:
# no data to parse: add empty columns and return directly
f = f.reindex(columns=list(f.columns) + list(key_mapping.values()))
return {"features": f, "projection": data["projection"]}

def parser(description):
pairs = dict(REGEX_KEYVALUE.findall(description))
return [autocast_value(pairs.get(key)) for key in key_mapping.keys()]
Expand Down
2 changes: 1 addition & 1 deletion dask_geomodeling/raster/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ def get_sources_and_requests(self, **request):

limit = self.limit
if self.limit is None:
limit = config.get("geomodeling.geometry_limit")
limit = config.get("geomodeling.geometry-limit")

geom_request = {
"mode": "intersects",
Expand Down
4 changes: 2 additions & 2 deletions dask_geomodeling/raster/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def get_sources_and_requests(self, **request):
"length": last_i - first_i + 1,
}
else:
raise RuntimeError(f"Unknown mode '{mode}'")
raise RuntimeError("Unknown mode '{}'".format(mode))
return [(process_kwargs, None)]

@staticmethod
Expand Down Expand Up @@ -421,7 +421,7 @@ def get_sources_and_requests(self, **request):
"length": last_i - first_i + 1,
}
else:
raise RuntimeError(f"Unknown mode '{mode}'")
raise RuntimeError("Unknown mode '{}'".format(mode))
return [(process_kwargs, None)]

@staticmethod
Expand Down
20 changes: 10 additions & 10 deletions dask_geomodeling/raster/temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,24 +381,24 @@ def __init__(
timezone="UTC",
):
if not isinstance(source, RasterBlock):
raise TypeError(f"'{type(source)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(source)))
if frequency is not None:
if not isinstance(frequency, str):
raise TypeError(f"'{type(frequency)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(frequency)))
frequency = to_offset(frequency).freqstr
if closed not in {None, "left", "right"}:
raise ValueError(f"closed must be None, 'left', or 'right'.")
raise ValueError("closed must be None, 'left', or 'right'.")
if label not in {None, "left", "right"}:
raise ValueError(f"label must be None, 'left', or 'right'.")
raise ValueError("label must be None, 'left', or 'right'.")
if not isinstance(timezone, str):
raise TypeError(f"'{type(timezone)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(timezone)))
timezone = pytz.timezone(timezone).zone
else:
closed = None
label = None
timezone = None
if not isinstance(statistic, str):
raise TypeError(f"'{type(statistic)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(statistic)))
# interpret percentile statistic
percentile = parse_percentile_statistic(statistic)
if percentile:
Expand Down Expand Up @@ -677,9 +677,9 @@ class Cumulative(BaseSingle):

def __init__(self, source, statistic="sum", frequency=None, timezone="UTC"):
if not isinstance(source, RasterBlock):
raise TypeError(f"'{type(source)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(source)))
if not isinstance(statistic, str):
raise TypeError(f"'{type(statistic)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(statistic)))
# interpret percentile statistic
percentile = parse_percentile_statistic(statistic)
if percentile:
Expand All @@ -688,10 +688,10 @@ def __init__(self, source, statistic="sum", frequency=None, timezone="UTC"):
raise ValueError("Unknown statistic '{}'".format(statistic))
if frequency is not None:
if not isinstance(frequency, str):
raise TypeError(f"'{type(frequency)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(frequency)))
frequency = to_offset(frequency).freqstr
if not isinstance(timezone, str):
raise TypeError(f"'{type(timezone)}' object is not allowed.")
raise TypeError("'{}' object is not allowed.".format(type(timezone)))
timezone = pytz.timezone(timezone).zone
else:
timezone = None
Expand Down

0 comments on commit ea58d10

Please sign in to comment.