From 911f69d2bfa0445b4067239c76f64b5ee1c955df Mon Sep 17 00:00:00 2001 From: m431m <4568458+m431m@users.noreply.github.com> Date: Thu, 16 Jan 2020 15:24:59 +0100 Subject: [PATCH] Version 1.0.0 - Python3 support --- COPYRIGHT.txt | 2 +- README.md | 6 +-- plugins/__init__.py | 6 +-- setup.py | 16 ++++---- src/__init__.py | 22 +++++++++++ src/extensions.py | 7 +--- src/metadata.py | 22 +++++------ src/mra.py | 91 +++++++++++++++++++++++-------------------- src/mra.yaml.sample | 2 +- src/mralogs.py | 11 ++---- src/pyhtml.py | 13 +++---- src/pyxml.py | 25 ++++++------ src/server.py | 32 +++++++-------- src/stores.py | 17 ++++---- src/tools.py | 5 +-- src/webapp.py | 21 +++++----- tests/__init__.py | 6 +-- tests/testScenario.py | 5 +-- tests/utils.py | 7 +--- 19 files changed, 153 insertions(+), 163 deletions(-) create mode 100644 src/__init__.py diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 6c1f109..7031d54 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -1,4 +1,4 @@ -Copyright (c) 2011-2013 Neogeo Technologies. +Copyright (C) 2011-2020 Neogeo Technologies. All Rights Reserved. MapServer Rest API is free software: you can redistribute it diff --git a/README.md b/README.md index 7b24c6c..aa370e2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MapServer Rest API **MapServer REST API** is a python wrapper around MapServer which allows to -manipulate a mapfile in a RESTFul way. It has been developped to match as +manipulate a mapfile in a RESTFul way. It has been developped to match as close as possible the way the GeoServer REST API acts. ## Installation @@ -10,8 +10,8 @@ See the Mapserver Rest API Documentation for installation instructions. ## Copying and license -**MapServer REST API** is copyright (c) 2011-2013 Neogeo Technologies. +**MapServer REST API** is Copyright (C) 2011-2020 Neogeo Technologies. It is free software licensed under the GNU General Public License version 3. You should have received a copy of the GNU General Public License along with -this program. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). \ No newline at end of file +this program. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). diff --git a/plugins/__init__.py b/plugins/__init__.py index 347ed97..b32a3a3 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -35,4 +32,3 @@ for module in os.listdir(os.path.dirname(__file__)): name, ext = os.path.splitext(module) if name != "__init__" and ext in ["", ".py"]: __all__.append(name) - diff --git a/setup.py b/setup.py index b11a9bb..964d2ee 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -29,11 +26,11 @@ from distutils.core import setup setup( name='MapServer Rest API', - version='0.1.0', + version='1.0.0', author='Neogeo Technologies', author_email='contact@neogeo-online.net', description='A RESTFul interface for MapServer', - long_description=file('README.md','rb').read(), + long_description=open('README.md', 'r').read(), keywords='neogeo mapserver rest restful', license="GPLv3", #url='', @@ -44,16 +41,17 @@ setup( 'License :: OSI Approved :: GPLv3', 'Operating System :: OS Independent', 'Programming Language :: Python', + 'Programming Language :: Python3', 'Natural Language :: English', 'Topic :: Scientific/Engineering :: GIS', ], #packages=, #package_dir={'':'src'}, #namespace_packages=['mra'], - requires=[ - 'web.py', + install_requires=[ + 'web.py==0.40', 'pyyaml', - 'osgeo', + 'gdal<2.5.0', ], scripts=[ 'src/server.py', diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..27b06b4 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,22 @@ +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # +# MapServer REST API is a python wrapper around MapServer which # +# allows to manipulate a mapfile in a RESTFul way. It has been # +# developped to match as close as possible the way the GeoServer # +# REST API acts. # +# # +# Copyright (C) 2011-2020 Neogeo Technologies. # +# # +# This file is part of MapServer Rest API. # +# # +# MapServer Rest API is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License # +# as published by the Free Software Foundation, either version 3 of # +# the License, or (at your option) any later version. # +# # +# MapServer Rest API is distributed in the hope that it will be # +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty # +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # diff --git a/src/extensions.py b/src/extensions.py index d2218cf..73bfd49 100644 --- a/src/extensions.py +++ b/src/extensions.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -47,7 +44,7 @@ class ExtensionManager(object): def load_plugins_dir(self, dir_path): path, pkg = os.path.split(os.path.abspath(dir_path)) pkg, _ = os.path.splitext(pkg) - print "Loading %s from %s" % (pkg, path) + print("Loading %s from %s" % (pkg, path)) sys.path.append(path) try: diff --git a/src/metadata.py b/src/metadata.py index 2416555..807eb19 100644 --- a/src/metadata.py +++ b/src/metadata.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -35,6 +32,7 @@ import yaml import contextlib from mapscript import MapServerError +import logging METADATA_NAME="mra" @@ -51,7 +49,8 @@ def get_metadata(obj, key, *args): try: value = obj.getMetaData(key) # We never now what mapscript might throws at us... - except MapServerError: + except MapServerError as e: + logging.warning("metadata.py::get_metadata MapServerError: %s", str(e)) value = None if value is None: @@ -84,18 +83,18 @@ def get_metadata_keys(obj): def set_metadata(obj, key, value): try: obj.setMetaData(key, value) - except UnicodeEncodeError, e: + except UnicodeEncodeError as e: obj.setMetaData(key, value.encode('utf8')) def set_metadatas(obj, metadatas): # TODO: erease all metadata first. - for key, value in metadatas.iteritems(): + for key, value in metadatas.items(): set_metadata(obj, key, value) def update_metadatas(obj, metadatas): - for key, value in metadatas.iteritems(): + for key, value in metadatas.items(): set_metadata(obj, key, value) @@ -140,18 +139,19 @@ def get_mra_metadata(obj, key, *args): try: return mra_metadata[key] - except KeyError: + except KeyError as e: + logging.warning("metadata.py::get_mra_metadata KeyError %s", str(e)) if not args: raise return args[0] def iter_mra_metadata_keys(obj): - return __get_mra(obj).iterkeys() + return iter(__get_mra(obj).keys()) def get_mra_metadata_keys(obj): - return __get_mra(obj).keys() + return list(__get_mra(obj).keys()) def update_mra_metadatas(obj, update): diff --git a/src/mra.py b/src/mra.py index bf87e1a..039af6c 100644 --- a/src/mra.py +++ b/src/mra.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -39,7 +36,7 @@ import os import os.path import string -import urlparse +import urllib.parse import functools import web import yaml @@ -51,6 +48,10 @@ from webapp import KeyExists import stores import metadata import xml.etree.ElementTree as ET +import logging + + +yaml.warnings({'YAMLLoadWarning': False}) # Defines commons outputformats: @@ -70,7 +71,7 @@ def outputformat( if transparent: _format.transparent = transparent if options: - for k, v in options.items(): + for k, v in list(options.items()): _format.setOption(k, v) return _format @@ -196,19 +197,19 @@ class Layer(MetadataMixin): return iter(self.get_fields()) def iter_classes(self): - for i in reversed(xrange(self.ms.numclasses)): + for i in reversed(range(self.ms.numclasses)): c = Clazz(self.ms.getClass(i)) c.index = i yield c def get_styles(self): - return set(self.ms.getClass(i).group for i in reversed(xrange(self.ms.numclasses))) + return set(self.ms.getClass(i).group for i in reversed(range(self.ms.numclasses))) def iter_styles(self): return iter(self.get_styles()) def get_SLD(self): - return self.ms.generateSLD().decode("LATIN1").encode("UTF8") + return self.ms.generateSLD().decode() def add_style_sld(self, mf, s_name, new_sld): # Because we do not want to apply the sld to real layers by mistake @@ -224,7 +225,8 @@ class Layer(MetadataMixin): try: xmlsld.firstChild.getElementsByTagNameNS("*", "NamedLayer")[0]\ .getElementsByTagNameNS("*", "Name")[0].firstChild.data = sld_layer_name - except: + except Exception as e: + logging.error("mra.py::Layer::add_style_sld: Bad sld (No NamedLayer/Name): %s", e) raise ValueError("Bad sld (No NamedLayer/Name)") # Remove encoding ? @@ -237,11 +239,12 @@ class Layer(MetadataMixin): mf.ms.insertLayer(ms_template_layer) try: - ms_template_layer.applySLD(new_sld.encode("utf-8"), sld_layer_name) - except: + ms_template_layer.applySLD(new_sld, sld_layer_name) + except Exception as e: + logging.error("mra.py::Layer::add_style_sld: Unable to access storage : %s", e) raise ValueError("Unable to access storage.") - for i in xrange(ms_template_layer.numclasses): + for i in range(ms_template_layer.numclasses): ms_class = ms_template_layer.getClass(i) ms_class.group = s_name self.ms.insertClass(ms_class) @@ -263,8 +266,9 @@ class Layer(MetadataMixin): return try: - style = open(os.path.join(os.path.dirname(__file__), "%s.sld" % s_name)).read() - except IOError, OSError: + style = open(os.path.join(os.path.dirname(__file__), "%s.sld" % s_name), encoding="utf-8").read() + except IOError as OSError: + logging.warning("mra.py::Layer::set_default_style IOError %s", OSError) return self.add_style_sld(mf, s_name, style) @@ -294,13 +298,13 @@ class LayerGroup(object): def add_layer(self, layer): layer.ms.group = self.name layer.set_metadata("wms_group_name", self.name) - for k, v in self.mapfile.get_mra_metadata("layergroups")[self.name].iteritems(): + for k, v in self.mapfile.get_mra_metadata("layergroups")[self.name].items(): layer.set_metadata("wms_group_%s" % k, v) self.mapfile.move_layer_down(layer.ms.name) def add(self, *args): for layer in args: - if isinstance(layer, basestring): + if isinstance(layer, str): layer = self.mapfile.get_layer(layer) self.add_layer(layer) @@ -313,7 +317,7 @@ class LayerGroup(object): def remove(self, *args): for layer in args: - if isinstance(layer, basestring): + if isinstance(layer, str): layer = mapfile.get_layer(layer) self.remove_layer(layer) @@ -361,17 +365,17 @@ class Mapfile(MetadataMixin): self.ms.setSize(1, 1) for outputformat in [ - v for k in OUTPUTFORMAT.keys() for v in list(OUTPUTFORMAT[k].values())]: + v for k in list(OUTPUTFORMAT.keys()) for v in list(OUTPUTFORMAT[k].values())]: self.ms.appendOutputFormat(outputformat) - for k, v in config.get('metadata', {}).iteritems(): + for k, v in config.get('metadata', {}).items(): self.set_metadata(k, v) for ows in ("ows", "wms", "wfs", "wcs"): self.set_metadata("%s_enable_request" % ows, "*") if 'onlineresource' in config: - onlineresource = urlparse.urljoin(config.get('onlineresource'), self.ms.name) + onlineresource = urllib.parse.urljoin(config.get('onlineresource'), self.ms.name) self.set_metadata('ows_onlineresource', onlineresource) fontset and self.ms.setFontSet(fontset) @@ -390,13 +394,13 @@ class Mapfile(MetadataMixin): def check(f, v): return f(v) if callable(f) else f == v - for l in xrange(self.ms.numlayers): + for l in range(self.ms.numlayers): ms_layer = self.ms.getLayer(l) - if not all(check(checker, getattr(ms_layer, k, None)) for k, checker in attr.iteritems()): + if not all(check(checker, getattr(ms_layer, k, None)) for k, checker in attr.items()): continue - if not all(check(checker, metadata.get_metadata(ms_layer, k, None)) for k, checker in meta.iteritems()): + if not all(check(checker, metadata.get_metadata(ms_layer, k, None)) for k, checker in meta.items()): continue - if not all(check(checker, metadata.get_mra_metadata(ms_layer, k, None)) for k, checker in mra.iteritems()): + if not all(check(checker, metadata.get_mra_metadata(ms_layer, k, None)) for k, checker in mra.items()): continue yield ms_layer @@ -407,7 +411,7 @@ class Mapfile(MetadataMixin): def get_layer(self, l_name): try: return next(self.iter_layers(attr={"name": l_name})) - except StopIteration: + except (StopIteration, SystemError): raise KeyError(l_name) def has_layer(self, l_name): @@ -464,7 +468,7 @@ class Mapfile(MetadataMixin): return LayerGroup(lg_name, self) def iter_layergroups(self): - return (LayerGroup(name, self) for name in self.get_mra_metadata("layergroups", {}).iterkeys()) + return (LayerGroup(name, self) for name in self.get_mra_metadata("layergroups", {}).keys()) def get_layergroup(self, lg_name): if lg_name in self.get_mra_metadata("layergroups", {}): @@ -542,7 +546,7 @@ class FeatureTypeModel(LayerModel): # TODO: clean up this fallback. else: self.ms.connectiontype = mapscript.MS_SHAPEFILE - url = urlparse.urlparse(cparam["url"]) + url = urllib.parse.urlparse(cparam["url"]) self.ms.data = self.ws.mra.get_file_path(url.path) # Update mra metadata, and make sure the mandatory ones are left untouched. @@ -602,7 +606,7 @@ class FeatureTypeModel(LayerModel): "gml_%s_type" % geometry_column: ft.get_geomtype_gml(), # TODO: Add gml_<geometry name>_occurances, "wfs_srs": ws.get_metadata("ows_srs"), - "wfs_getfeature_formatlist": ",".join(OUTPUTFORMAT["WFS"].keys()) + "wfs_getfeature_formatlist": ",".join(list(OUTPUTFORMAT["WFS"].keys())) }) if ft.get_fid_column() is not None: @@ -639,7 +643,7 @@ class CoverageModel(LayerModel): #if cparam["dbtype"] in ["tif", "tiff"]: self.ms.connectiontype = mapscript.MS_RASTER - url = urlparse.urlparse(cparam["url"]) + url = urllib.parse.urlparse(cparam["url"]) self.ms.data = self.ws.mra.get_file_path(url.path) # TODO: strip extention. #else: @@ -731,10 +735,10 @@ class Workspace(Mapfile): return info def iter_store_names(self, st_type): - return self.get_mra_metadata("%ss" % st_type, {}).iterkeys() + return iter(self.get_mra_metadata("%ss" % st_type, {}).keys()) def iter_stores(self, st_type): - return self.get_mra_metadata("%ss" % st_type, {}).iteritems() + return iter(self.get_mra_metadata("%ss" % st_type, {}).items()) def create_store(self, st_type, name, configuration): with self.mra_metadata("%ss" % st_type, {}) as stores: @@ -791,7 +795,7 @@ class Workspace(Mapfile): try: next(self.iter_featuretypemodels(name)) - except StopIteration: + except (StopIteration, SystemError): pass # No layers use our store, all OK. else: raise ValueError("The datastore \"%s\" can't be delete because it is used." % name) @@ -834,7 +838,7 @@ class Workspace(Mapfile): try: next(self.iter_coveragemodels(name)) - except StopIteration: + except (StopIteration, SystemError): pass # No layers use our store, all OK. else: raise ValueError("The coveragestore \"%s\" can't be delete because it is used." % name) @@ -870,7 +874,7 @@ class Workspace(Mapfile): def get_layermodel(self, st_type, store, name): try: return next(self.iter_layermodels(attr={"name": self.__model_name(st_type, store, name)})) - except StopIteration: + except (StopIteration, SystemError): raise KeyError((st_type, store, name)) def has_layermodel(self, st_type, name, store): @@ -946,7 +950,7 @@ class Workspace(Mapfile): class MRA(object): def __init__(self, config_path): try: - self.config = yaml.load(open(config_path, "r")) + self.config = yaml.load(open(config_path, "r"), Loader=yaml.FullLoader) except yaml.YAMLError as e: exit("Error in configuration file: %s" % e) @@ -987,7 +991,8 @@ class MRA(object): def list_fontset(self): try: return [line.split()[0] for line in open(self.get_fontset_path(), "r")] - except: + except Exception as e: + logging.warn('mra.py::MRA::list_fontset error %s', e) return [] def update_fontset(self): @@ -1039,18 +1044,18 @@ class MRA(object): def create_style(self, name, data): path = self.get_style_path("%s.sld" % name) - with open(self.mk_path(path), "w") as f: + with open(self.mk_path(path), "wb") as f: f.write(data) return path def get_style(self, name): try: return ET.tostring( - ET.parse(self.get_style_path("%s.sld" % name)).getroot()) + ET.parse(self.get_style_path("%s.sld" % name)).getroot()).decode() except (OSError, IOError): if name in ["default_point", "default_line", "default_polygon"]: return ET.tostring(ET.parse( - os.path.join(os.path.dirname(__file__), "%s.sld" % name)).getroot()) + os.path.join(os.path.dirname(__file__), "%s.sld" % name)).getroot()).decode() raise KeyError(name) def delete_style(self, name): @@ -1109,7 +1114,7 @@ class MRA(object): path = self.get_available_path("%s.ws.map" % name) try: return Workspace(self, path) - except IOError, OSError: + except IOError as OSError: raise KeyError(name) def delete_workspace(self, name): @@ -1142,7 +1147,7 @@ class MRA(object): # URL Helpers: def href_parse(self, href, nb): - url = urlparse.urlparse(href) + url = urllib.parse.urlparse(href) parts = url.path.split("/")[-nb:] if parts: parts[-1] = parts[-1].rsplit(".", 1)[0] @@ -1159,7 +1164,7 @@ class MRA(object): url += " ".join("%s=%s" % (p, cparam[p]) for p in ["user", "password"] if p in cparam) return url elif "url" in cparam: - url = urlparse.urlparse(cparam["url"]) + url = urllib.parse.urlparse(cparam["url"]) if url.scheme != "file" or url.netloc: raise ValueError("Only local files are suported.") return self.get_file_path(url.path) diff --git a/src/mra.yaml.sample b/src/mra.yaml.sample index 3968d30..1867c64 100644 --- a/src/mra.yaml.sample +++ b/src/mra.yaml.sample @@ -29,7 +29,7 @@ mapfile: units: "DD" # onlineresource: "http://... metadata: - ows_srs: ["EPSG:4326", "EPSG:3857"] + ows_srs: "EPSG:4326 EPSG:3857" debug: ## web_debug allows for easy debuging in the the browser, should be deactivated in production. diff --git a/src/mralogs.py b/src/mralogs.py index 4a9a1a2..aad5685 100644 --- a/src/mralogs.py +++ b/src/mralogs.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -109,11 +106,11 @@ def logIn(level="debug", filter=(lambda *a, **kw:True)): def decorator(f): @functools.wraps(f) def wrapper(*args, **kwargs): - if filter(*args, **kwargs): + if list(filter(*args, **kwargs)): arguments = inspect.getcallargs(getattr(f, "original_function", f), *args, **kwargs) getattr(logging, level, "error")("function \"%s\" was called with args: %s" % (f.__name__, dict([(a, short_str(v)) for a, v - in arguments.iteritems()]))) + in arguments.items()]))) return f(*args, **kwargs) wrapper.original_function = f return wrapper @@ -137,7 +134,7 @@ def logOut(level="debug", filter=(lambda *a, **kw:True)): @functools.wraps(f) def wrapper(*args, **kwargs): ret = f(*args, **kwargs) - if filter(ret, *args, **kwargs): + if list(filter(ret, *args, **kwargs)): getattr(logging, level, "error")("function \"%s\" returned: %s" % (f.__name__, short_str(ret))) return ret wrapper.original_function = f diff --git a/src/pyhtml.py b/src/pyhtml.py index 9dec3b6..7421c09 100644 --- a/src/pyhtml.py +++ b/src/pyhtml.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -30,8 +27,8 @@ """ import pyxml -import StringIO -import urlparse +import io +import urllib.parse from xml.etree import ElementTree as etree from cgi import escape from xml.sax.saxutils import unescape @@ -42,7 +39,7 @@ def should_be_url(s): consider a string is a URL. """ - parsed = urlparse.urlparse(s) + parsed = urllib.parse.urlparse(s) return parsed.scheme and parsed.netloc @@ -113,7 +110,7 @@ def dump(obj, fp, indent=0, *args, **kwargs): def dumps(obj, *args, **kwargs): """Returns the html representation of obj as a string.""" - stream = StringIO.StringIO() + stream = io.StringIO() dump(obj, stream, *args, **kwargs) stream.flush() return stream.getvalue() diff --git a/src/pyxml.py b/src/pyxml.py index a8550a9..27cd8f4 100644 --- a/src/pyxml.py +++ b/src/pyxml.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -140,13 +137,13 @@ def default_xml_mapper(obj, obj_name, return dict_mapper(obj_name) elif isinstance(obj, list) or isinstance(obj, tuple): return list_mapper(obj_name) - elif any(isinstance(obj, t) for t in (basestring, int, float)): + elif any(isinstance(obj, t) for t in (str, int, float)): # Those we are sure we want to map as strings. return xml_string, None elif hasattr(obj, "__str__"): # Those we render as strings, but we are not sure. # Just add the type to the case above it is justified! - print "xml_mapper: Warning: We are trying to map %s as a string." % (type(obj)) + print("xml_mapper: Warning: We are trying to map %s as a string." % (type(obj))) return xml_string, None else: raise NotImplementedError("Can't map %s object." % type(obj)) @@ -168,7 +165,7 @@ def xml(obj, obj_name=None, parent=None, # Create the parent if it's not provided. if parent is None: - parent = etree.Element(tag=obj_name) + parent = etree.Element(obj_name) mapper, hint = xml_mapper(obj, obj_name, dict_mapper, list_mapper) if not mapper: @@ -198,11 +195,11 @@ def xml_dict(parent, obj, hint=None, xml_mapper=default_xml_mapper, The entries are of the form: <key>value</key> or <hint[0] hint[1]=key>value</hint[0]> """ - for k, v in obj.iteritems(): + for k, v in obj.items(): if hint: - child = etree.Element(tag=hint[0], attrib={hint[1]:k}) + child = etree.Element(hint[0], attrib={hint[1]:k}) else: - child = etree.Element(tag=k, attrib={}) + child = etree.Element(k, attrib={}) xml(v, parent=child, xml_mapper=xml_mapper, dict_mapper=dict_mapper, list_mapper=list_mapper) parent.append(child) @@ -215,7 +212,7 @@ def xml_list(parent, obj, hint, xml_mapper=default_xml_mapper, """ for v in obj: - child = etree.Element(tag=hint, attrib={}) + child = etree.Element(hint, attrib={}) xml(v, parent=child, xml_mapper=xml_mapper, dict_mapper=dict_mapper, list_mapper=list_mapper) parent.append(child) @@ -294,7 +291,8 @@ def loads(s, retname=False, *args, **kwargs): try: xml = etree.fromstring(s) # Python 2.6 has no xml.etree.ElementTree.ParseError. - except BaseException: + except BaseException as e: + logging.error("pyxml.py::loads: No XML object could be decoded. %s", e) raise ValueError("No XML object could be decoded.") o = obj(xml, *args, **kwargs) return (o, xml.tag) if retname else o @@ -308,7 +306,8 @@ def load(fp, retname=False, *args, **kwargs): try: xml = etree.parse(fp) # Python 2.6 has no xml.etree.ElementTree.ParseError. - except BaseException: + except BaseException as e: + logging.error("pyxml.py::load: No XML object could be decoded. %s", e) raise ValueError("No XML object could be decoded.") o = obj(xml, *args, **kwargs) return (o, xml.tag) if retname else o diff --git a/src/server.py b/src/server.py index 082687a..551ee78 100755 --- a/src/server.py +++ b/src/server.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -34,7 +31,7 @@ import os.path import sys import web import json -import urlparse +import urllib.parse as urlparse import logging import mralogs from mra import MRA @@ -116,7 +113,7 @@ class workspaces(object): ws_name = data.pop("name") ws_metadata = dict( - ("ows_%s" % k, v) for k, v in data.iteritems() if k in ["title", "abstract"]) + ("ows_%s" % k, v) for k, v in data.items() if k in ["title", "abstract"]) if "srs" in data: ws_metadata["ows_srs"] = " ".join(data["srs"]) @@ -296,7 +293,7 @@ class featuretypes(object): l_enabled = data.pop("enabled", True) l_metadata = dict( - ("ows_%s" % k, v) for k, v in data.iteritems() if k in ["title", "abstract"]) + ("ows_%s" % k, v) for k, v in data.items() if k in ["title", "abstract"]) # Creates first the feature type: with webapp.mightConflict("featureType", datastore=ds_name): @@ -428,7 +425,7 @@ class featuretype(object): if ft_name != data["name"]: raise webapp.Forbidden("Can't change the name of a feature type.") - metadata = dict((k, v) for k, v in data.iteritems() if k in ["title", "abstract"]) + metadata = dict((k, v) for k, v in data.items() if k in ["title", "abstract"]) with webapp.mightNotFound("featureType", datastore=ds_name): ws.update_featuretypemodel(ds_name, ft_name, metadata) @@ -589,7 +586,7 @@ class coverages(object): l_enabled = data.pop("enabled", True) l_metadata = dict( - ("ows_%s" % k, v) for k, v in data.iteritems() if k in ["title", "abstract"]) + ("ows_%s" % k, v) for k, v in data.items() if k in ["title", "abstract"]) # Creates first the coverage: with webapp.mightConflict("coverage", coveragestore=cs_name): @@ -702,7 +699,7 @@ class coverage(object): if c_name != data["name"]: raise webapp.Forbidden("Can't change the name of a coverage.") - metadata = dict((k, v) for k, v in data.iteritems() if k in ["title", "abstract"]) + metadata = dict((k, v) for k, v in data.items() if k in ["title", "abstract"]) with webapp.mightNotFound("coverage", coveragestore=cs_name): ws.update_coveragemodel(c_name, cs_name, metadata) @@ -968,8 +965,9 @@ class layers(object): style = mra.get_style(s_name) layer = mf.get_layer(l_name) wslayer = wsmf.get_layer(l_name) - layer.add_style_sld(mf, s_name, style) - wslayer.add_style_sld(wsmf, s_name, style) + with webapp.mightFailLookup(exceptions=(Exception,)): + layer.add_style_sld(mf, s_name, style) + wslayer.add_style_sld(wsmf, s_name, style) # Remove the automatic default style. for s_name in layer.iter_styles(): @@ -1133,10 +1131,10 @@ class layer(object): style = mra.get_style(s_name) layer.remove_style(s_name) - layer.add_style_sld(mf, s_name, style) - wslayer.remove_style(s_name) - wslayer.add_style_sld(wsmf, s_name, style) + with webapp.mightFailLookup(exceptions=(Exception,)): + layer.add_style_sld(mf, s_name, style) + wslayer.add_style_sld(wsmf, s_name, style) # Remove the automatic default style. for s_name in layer.iter_styles(): @@ -1420,7 +1418,7 @@ class layergroup(object): raise webapp.Forbidden("Can't change the name of a layergroup.") layers = data.pop("layers", []) - if not isinstance(layers, list) or any(not isinstance(x, basestring) for x in layers): + if not isinstance(layers, list) or any(not isinstance(x, str) for x in layers): raise webapp.BadRequest("layers must be a list of layer names.") lg.clear() @@ -1527,7 +1525,7 @@ class workspaceLayergroup(object): raise webapp.Forbidden("Can't change the name of a layergroup.") layers = data.pop("layers", []) - if not isinstance(layers, list) or any(not isinstance(x, basestring) for x in layers): + if not isinstance(layers, list) or any(not isinstance(x, str) for x in layers): raise webapp.BadRequest("layers must be a list of layer names.") lg.clear() diff --git a/src/stores.py b/src/stores.py index 5c9dc8a..088c1ff 100644 --- a/src/stores.py +++ b/src/stores.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -275,7 +272,7 @@ class Featuretype(object): return list(self.iterfields()) def iterfields(self): - for i in xrange(self.backend.GetLayerDefn().GetFieldCount()): + for i in range(self.backend.GetLayerDefn().GetFieldCount()): yield Field(self.backend.GetLayerDefn().GetFieldDefn(i), self) def nbfeatures(self): @@ -284,7 +281,7 @@ class Featuretype(object): def iterfeatures(self, what=[], when={}): if what != [] or when != {}: raise NotImplementedError("Iterfeature doesn't support filters yet.") - for i in xrange(self.backend.GetFeatureCount()): + for i in range(self.backend.GetFeatureCount()): yield Feature(self.backend.GetFeature(i), self) def get_aditional_info(self): @@ -298,7 +295,7 @@ class Featuretype(object): (schema, table)) if not result: return - for i in xrange(result.GetFeatureCount()): + for i in range(result.GetFeatureCount()): feature = result.GetFeature(i) name, nullable = feature.GetField(0), feature.GetField(1) self.nullables[name] = nullable @@ -339,7 +336,7 @@ class Datastore(object): else: if self.schema: key = "%s.%s" % (self.schema, key) - item = self.backend.GetLayerByName(key.encode("ascii", "ignore")) + item = self.backend.GetLayerByName(key) if item == None: raise KeyError(key) return Featuretype(item, self) @@ -347,7 +344,7 @@ class Datastore(object): return self.backend.GetLayerCount() def iterlayers(self): - for i in xrange(self.backend.GetLayerCount()): + for i in range(self.backend.GetLayerCount()): yield Featuretype(self.backend.GetLayerByIndex(i), self) @@ -428,5 +425,5 @@ class Coveragestore(object): return list(self.iterbands()) def iterbands(self): - for i in xrange(1, self.backend.RasterCount + 1): + for i in range(1, self.backend.RasterCount + 1): yield Band(self.backend.GetRasterBand(i)) diff --git a/src/tools.py b/src/tools.py index 5530358..50f8c28 100644 --- a/src/tools.py +++ b/src/tools.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # diff --git a/src/webapp.py b/src/webapp.py index 8193b45..6d03ad4 100644 --- a/src/webapp.py +++ b/src/webapp.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -38,6 +35,7 @@ import functools import os.path import itertools import mralogs +import logging class KeyExists(KeyError): @@ -253,7 +251,7 @@ class URLMap(object): if not last_is_var: url += "(?:/|(\\.[^/.]+)?)" - self.map.extend((url, page if isinstance(page, basestring) else page.__name__)) + self.map.extend((url, page if isinstance(page, str) else page.__name__)) def __getattr__(self, name): """Maps all attributes to a wrapper function calling self(name, *args, **kwargs), @@ -377,7 +375,7 @@ class HTTPCompatible(object): args = list(args) - if isinstance(args[-1], basestring): + if isinstance(args[-1], str): last = args[-1].split(".") if len(last) == 1: last.append(self.default) @@ -424,13 +422,14 @@ class HTTPCompatible(object): try: content = f(*args, **kwargs) except BaseException as e: + logging.warn("webapp.py::HTTPCompatible Unknown Error : %s", e) raise name_hint = self.name_hint if name_hint is None and isinstance(content, dict) and len(content) == 1: - name_hint = next(content.iterkeys()) - content = next(content.itervalues()) + name_hint = next(iter(content.keys())) + content = next(iter(content.values())) elif name_hint is None: name_hint = "response" @@ -477,11 +476,11 @@ def get_data(name=None, mandatory=[], authorized=[], forbidden=[]): try: if "text/xml" in ctype or "application/xml" in ctype: data, dname = pyxml.loads(data, retname=True) - print "received \"%s\"" % dname - print data + print("received \"%s\"" % dname) + print(data) if name and dname != name: data = None elif "application/json" in ctype: - data = json.loads(data) + data = json.loads(data.decode()) if name: data = data.get(name, None) else: raise web.badrequest("Content-type \"%s\" is not allowed." % ctype) diff --git a/tests/__init__.py b/tests/__init__.py index 3891fd0..27b06b4 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -23,4 +20,3 @@ # GNU General Public License for more details. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - diff --git a/tests/testScenario.py b/tests/testScenario.py index 7f22ba5..5fc5a4e 100644 --- a/tests/testScenario.py +++ b/tests/testScenario.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # diff --git a/tests/utils.py b/tests/utils.py index d6d9250..41ac318 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python2.7 -# -*- coding: utf-8 -*- - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MapServer REST API is a python wrapper around MapServer which # @@ -8,7 +5,7 @@ # developped to match as close as possible the way the GeoServer # # REST API acts. # # # -# Copyright (C) 2011-2013 Neogeo Technologies. # +# Copyright (C) 2011-2020 Neogeo Technologies. # # # # This file is part of MapServer Rest API. # # # @@ -88,5 +85,3 @@ def APIRequest(method, url, data=None, encode=default_encoding, decode=default_e assert 200 <= r.status < 300, recv return (recv, r) if get_response else recv - - -- GitLab