MapProxy 1.16.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mapproxy/__init__.py +0 -0
- mapproxy/cache/__init__.py +36 -0
- mapproxy/cache/azureblob.py +145 -0
- mapproxy/cache/base.py +111 -0
- mapproxy/cache/compact.py +664 -0
- mapproxy/cache/couchdb.py +295 -0
- mapproxy/cache/dummy.py +34 -0
- mapproxy/cache/file.py +185 -0
- mapproxy/cache/geopackage.py +609 -0
- mapproxy/cache/legend.py +83 -0
- mapproxy/cache/mbtiles.py +392 -0
- mapproxy/cache/meta.py +78 -0
- mapproxy/cache/path.py +250 -0
- mapproxy/cache/redis.py +88 -0
- mapproxy/cache/renderd.py +95 -0
- mapproxy/cache/riak.py +202 -0
- mapproxy/cache/s3.py +177 -0
- mapproxy/cache/tile.py +699 -0
- mapproxy/client/__init__.py +0 -0
- mapproxy/client/arcgis.py +79 -0
- mapproxy/client/cgi.py +139 -0
- mapproxy/client/http.py +315 -0
- mapproxy/client/log.py +33 -0
- mapproxy/client/tile.py +150 -0
- mapproxy/client/wms.py +254 -0
- mapproxy/compat/__init__.py +46 -0
- mapproxy/compat/image.py +79 -0
- mapproxy/compat/itertools.py +29 -0
- mapproxy/compat/modules.py +13 -0
- mapproxy/config/__init__.py +22 -0
- mapproxy/config/config.py +201 -0
- mapproxy/config/coverage.py +107 -0
- mapproxy/config/defaults.py +98 -0
- mapproxy/config/loader.py +2286 -0
- mapproxy/config/spec.py +644 -0
- mapproxy/config/validator.py +239 -0
- mapproxy/config_template/__init__.py +0 -0
- mapproxy/config_template/base_config/config.wsgi +10 -0
- mapproxy/config_template/base_config/full_example.yaml +593 -0
- mapproxy/config_template/base_config/full_seed_example.yaml +79 -0
- mapproxy/config_template/base_config/log.ini +35 -0
- mapproxy/config_template/base_config/mapproxy.yaml +60 -0
- mapproxy/config_template/base_config/seed.yaml +27 -0
- mapproxy/exception.py +142 -0
- mapproxy/featureinfo.py +252 -0
- mapproxy/grid.py +1170 -0
- mapproxy/image/__init__.py +536 -0
- mapproxy/image/fonts/DejaVuSans.ttf +0 -0
- mapproxy/image/fonts/DejaVuSansMono.ttf +0 -0
- mapproxy/image/fonts/LICENSE +99 -0
- mapproxy/image/fonts/__init__.py +0 -0
- mapproxy/image/mask.py +75 -0
- mapproxy/image/merge.py +316 -0
- mapproxy/image/message.py +347 -0
- mapproxy/image/opts.py +182 -0
- mapproxy/image/tile.py +167 -0
- mapproxy/image/transform.py +350 -0
- mapproxy/layer.py +470 -0
- mapproxy/multiapp.py +231 -0
- mapproxy/proj.py +302 -0
- mapproxy/request/__init__.py +18 -0
- mapproxy/request/arcgis.py +259 -0
- mapproxy/request/base.py +476 -0
- mapproxy/request/tile.py +128 -0
- mapproxy/request/wms/__init__.py +793 -0
- mapproxy/request/wms/exception.py +99 -0
- mapproxy/request/wmts.py +436 -0
- mapproxy/response.py +237 -0
- mapproxy/script/__init__.py +0 -0
- mapproxy/script/conf/__init__.py +0 -0
- mapproxy/script/conf/app.py +195 -0
- mapproxy/script/conf/caches.py +45 -0
- mapproxy/script/conf/layers.py +54 -0
- mapproxy/script/conf/seeds.py +37 -0
- mapproxy/script/conf/sources.py +86 -0
- mapproxy/script/conf/utils.py +143 -0
- mapproxy/script/defrag.py +184 -0
- mapproxy/script/export.py +333 -0
- mapproxy/script/grids.py +188 -0
- mapproxy/script/scales.py +126 -0
- mapproxy/script/util.py +406 -0
- mapproxy/script/wms_capabilities.py +152 -0
- mapproxy/seed/__init__.py +0 -0
- mapproxy/seed/cachelock.py +121 -0
- mapproxy/seed/cleanup.py +187 -0
- mapproxy/seed/config.py +469 -0
- mapproxy/seed/script.py +388 -0
- mapproxy/seed/seeder.py +538 -0
- mapproxy/seed/spec.py +64 -0
- mapproxy/seed/util.py +254 -0
- mapproxy/service/__init__.py +14 -0
- mapproxy/service/base.py +46 -0
- mapproxy/service/demo.py +356 -0
- mapproxy/service/kml.py +331 -0
- mapproxy/service/ows.py +38 -0
- mapproxy/service/template_helper.py +53 -0
- mapproxy/service/templates/demo/capabilities_demo.html +16 -0
- mapproxy/service/templates/demo/demo.html +181 -0
- mapproxy/service/templates/demo/openlayers-demo.cfg +16 -0
- mapproxy/service/templates/demo/static/img/blank.gif +0 -0
- mapproxy/service/templates/demo/static/img/east-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/north-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/south-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/west-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-minus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-plus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-world-mini.png +0 -0
- mapproxy/service/templates/demo/static/logo.png +0 -0
- mapproxy/service/templates/demo/static/ol.css +345 -0
- mapproxy/service/templates/demo/static/ol.js +4 -0
- mapproxy/service/templates/demo/static/proj4.min.js +1 -0
- mapproxy/service/templates/demo/static/proj4defs.js +1 -0
- mapproxy/service/templates/demo/static/site.css +137 -0
- mapproxy/service/templates/demo/static/theme/default/framedCloud.css +0 -0
- mapproxy/service/templates/demo/static/theme/default/google.css +17 -0
- mapproxy/service/templates/demo/static/theme/default/ie6-style.css +10 -0
- mapproxy/service/templates/demo/static/theme/default/style.css +482 -0
- mapproxy/service/templates/demo/static.html +34 -0
- mapproxy/service/templates/demo/tms_demo.html +103 -0
- mapproxy/service/templates/demo/wms_demo.html +140 -0
- mapproxy/service/templates/demo/wmts_demo.html +110 -0
- mapproxy/service/templates/tms_capabilities.xml +13 -0
- mapproxy/service/templates/tms_exception.xml +4 -0
- mapproxy/service/templates/tms_root_resource.xml +7 -0
- mapproxy/service/templates/tms_tilemap_capabilities.xml +14 -0
- mapproxy/service/templates/wms100capabilities.xml +112 -0
- mapproxy/service/templates/wms100exception.xml +4 -0
- mapproxy/service/templates/wms110capabilities.xml +152 -0
- mapproxy/service/templates/wms110exception.xml +5 -0
- mapproxy/service/templates/wms111capabilities.xml +183 -0
- mapproxy/service/templates/wms111exception.xml +5 -0
- mapproxy/service/templates/wms130capabilities.xml +326 -0
- mapproxy/service/templates/wms130exception.xml +8 -0
- mapproxy/service/templates/wmts100capabilities.xml +155 -0
- mapproxy/service/templates/wmts100exception.xml +9 -0
- mapproxy/service/tile.py +536 -0
- mapproxy/service/wms.py +851 -0
- mapproxy/service/wmts.py +381 -0
- mapproxy/source/__init__.py +75 -0
- mapproxy/source/arcgis.py +39 -0
- mapproxy/source/error.py +39 -0
- mapproxy/source/mapnik.py +259 -0
- mapproxy/source/tile.py +96 -0
- mapproxy/source/wms.py +270 -0
- mapproxy/srs.py +726 -0
- mapproxy/template.py +54 -0
- mapproxy/test/__init__.py +0 -0
- mapproxy/test/conftest.py +7 -0
- mapproxy/test/helper.py +247 -0
- mapproxy/test/http.py +494 -0
- mapproxy/test/image.py +210 -0
- mapproxy/test/mocker.py +2268 -0
- mapproxy/test/schemas/inspire/common/1.0/common.xsd +1461 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_bul.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_cze.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dan.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dut.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_eng.xsd +155 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_est.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fin.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ger.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gle.xsd +109 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_hun.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ita.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lav.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lit.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_mlt.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_pol.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_por.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_rum.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slo.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slv.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_spa.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_swe.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/network.xsd +521 -0
- mapproxy/test/schemas/inspire/inspire_vs/1.0/inspire_vs.xsd +19 -0
- mapproxy/test/schemas/kml/2.2.0/ReadMe.txt +14 -0
- mapproxy/test/schemas/kml/2.2.0/atom-author-link.xsd +66 -0
- mapproxy/test/schemas/kml/2.2.0/ogckml22.xsd +1646 -0
- mapproxy/test/schemas/kml/2.2.0/xAL.xsd +1680 -0
- mapproxy/test/schemas/ows/1.1.0/ReadMe.txt +87 -0
- mapproxy/test/schemas/ows/1.1.0/ows19115subset.xsd +235 -0
- mapproxy/test/schemas/ows/1.1.0/owsAll.xsd +23 -0
- mapproxy/test/schemas/ows/1.1.0/owsCommon.xsd +157 -0
- mapproxy/test/schemas/ows/1.1.0/owsContents.xsd +86 -0
- mapproxy/test/schemas/ows/1.1.0/owsDataIdentification.xsd +127 -0
- mapproxy/test/schemas/ows/1.1.0/owsDomainType.xsd +279 -0
- mapproxy/test/schemas/ows/1.1.0/owsExceptionReport.xsd +76 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetCapabilities.xsd +112 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetResourceByID.xsd +51 -0
- mapproxy/test/schemas/ows/1.1.0/owsInputOutputData.xsd +59 -0
- mapproxy/test/schemas/ows/1.1.0/owsManifest.xsd +125 -0
- mapproxy/test/schemas/ows/1.1.0/owsOperationsMetadata.xsd +140 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceIdentification.xsd +60 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceProvider.xsd +47 -0
- mapproxy/test/schemas/sld/1.1.0/sld_capabilities.xsd +27 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.dtd +353 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.xml +188 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.dtd +524 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.xml +260 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.dtd +273 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.xml +303 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.xml +33 -0
- mapproxy/test/schemas/wms/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd +274 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wms/1.3.0/ReadMe.txt +8 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xml +277 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xsd +611 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xml +34 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xsd +28 -0
- mapproxy/test/schemas/wmsc/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_MS_Capabilities.dtd +283 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wmts/1.0/ReadMe.txt +32 -0
- mapproxy/test/schemas/wmts/1.0/wmts.xsd +28 -0
- mapproxy/test/schemas/wmts/1.0/wmtsAbstract.wsdl +151 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_request.xsd +38 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_response.xsd +564 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_request.xsd +57 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_response.xsd +72 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetTile_request.xsd +91 -0
- mapproxy/test/schemas/wmts/1.0/wmtsKVP.xsd +76 -0
- mapproxy/test/schemas/wmts/1.0/wmtsPayload_response.xsd +70 -0
- mapproxy/test/schemas/xlink/1.0.0/ReadMe.txt +6 -0
- mapproxy/test/schemas/xlink/1.0.0/xlinks.xsd +122 -0
- mapproxy/test/schemas/xml.xsd +287 -0
- mapproxy/test/system/__init__.py +98 -0
- mapproxy/test/system/fixture/arcgis.yaml +57 -0
- mapproxy/test/system/fixture/auth.yaml +70 -0
- mapproxy/test/system/fixture/cache.mbtiles +0 -0
- mapproxy/test/system/fixture/cache_azureblob.yaml +59 -0
- mapproxy/test/system/fixture/cache_band_merge.yaml +73 -0
- mapproxy/test/system/fixture/cache_bulk_meta_tiles.yaml +24 -0
- mapproxy/test/system/fixture/cache_data/dop_cache_EPSG3857/00/000/000/000/000/000/000.png +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_EPSG900913/01/000/000/000/000/000/001.jpeg +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_transparent_EPSG900913/01/000/000/000/000/000/001.png +0 -0
- mapproxy/test/system/fixture/cache_geopackage.yaml +56 -0
- mapproxy/test/system/fixture/cache_grid_names.yaml +50 -0
- mapproxy/test/system/fixture/cache_mbtiles.yaml +28 -0
- mapproxy/test/system/fixture/cache_s3.yaml +58 -0
- mapproxy/test/system/fixture/cache_source.yaml +81 -0
- mapproxy/test/system/fixture/combined_sources.yaml +130 -0
- mapproxy/test/system/fixture/coverage.yaml +77 -0
- mapproxy/test/system/fixture/demo.yaml +135 -0
- mapproxy/test/system/fixture/dimension.yaml +59 -0
- mapproxy/test/system/fixture/disable_storage.yaml +25 -0
- mapproxy/test/system/fixture/empty_ogrdata.geojson +1 -0
- mapproxy/test/system/fixture/formats.yaml +72 -0
- mapproxy/test/system/fixture/inspire.yaml +101 -0
- mapproxy/test/system/fixture/inspire_full.yaml +124 -0
- mapproxy/test/system/fixture/kml_layer.yaml +66 -0
- mapproxy/test/system/fixture/layer.yaml +260 -0
- mapproxy/test/system/fixture/layergroups.yaml +57 -0
- mapproxy/test/system/fixture/layergroups_root.yaml +106 -0
- mapproxy/test/system/fixture/legendgraphic.yaml +93 -0
- mapproxy/test/system/fixture/mapnik_source.yaml +66 -0
- mapproxy/test/system/fixture/mapproxy_export.yaml +12 -0
- mapproxy/test/system/fixture/mapserver.yaml +23 -0
- mapproxy/test/system/fixture/minimal_cgi.py +16 -0
- mapproxy/test/system/fixture/mixed_mode.yaml +49 -0
- mapproxy/test/system/fixture/multi_cache_layers.yaml +100 -0
- mapproxy/test/system/fixture/multiapp1.yaml +20 -0
- mapproxy/test/system/fixture/multiapp2.yaml +19 -0
- mapproxy/test/system/fixture/renderd_client.yaml +55 -0
- mapproxy/test/system/fixture/scalehints.yaml +70 -0
- mapproxy/test/system/fixture/seed.yaml +94 -0
- mapproxy/test/system/fixture/seed_mapproxy.yaml +39 -0
- mapproxy/test/system/fixture/seed_old.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts_mapproxy.yaml +27 -0
- mapproxy/test/system/fixture/seedonly.yaml +51 -0
- mapproxy/test/system/fixture/sld.yaml +35 -0
- mapproxy/test/system/fixture/source_errors.yaml +84 -0
- mapproxy/test/system/fixture/source_errors_raise.yaml +82 -0
- mapproxy/test/system/fixture/tileservice_origin.yaml +26 -0
- mapproxy/test/system/fixture/tileservice_refresh.yaml +59 -0
- mapproxy/test/system/fixture/tilesource_minmax_res.yaml +22 -0
- mapproxy/test/system/fixture/util-conf-base-grids.yaml +5 -0
- mapproxy/test/system/fixture/util-conf-overwrite.yaml +13 -0
- mapproxy/test/system/fixture/util-conf-wms-111-cap.xml +90 -0
- mapproxy/test/system/fixture/util_grids.yaml +30 -0
- mapproxy/test/system/fixture/util_wms_capabilities111.xml +130 -0
- mapproxy/test/system/fixture/util_wms_capabilities130.xml +100 -0
- mapproxy/test/system/fixture/util_wms_capabilities_service_exception.xml +5 -0
- mapproxy/test/system/fixture/watermark.yaml +50 -0
- mapproxy/test/system/fixture/wms_srs_extent.yaml +39 -0
- mapproxy/test/system/fixture/wms_versions.yaml +38 -0
- mapproxy/test/system/fixture/wmts.yaml +134 -0
- mapproxy/test/system/fixture/wmts_dimensions.yaml +57 -0
- mapproxy/test/system/fixture/xslt_featureinfo.yaml +54 -0
- mapproxy/test/system/fixture/xslt_featureinfo_input.yaml +51 -0
- mapproxy/test/system/test_arcgis.py +156 -0
- mapproxy/test/system/test_auth.py +1134 -0
- mapproxy/test/system/test_behind_proxy.py +75 -0
- mapproxy/test/system/test_bulk_meta_tiles.py +106 -0
- mapproxy/test/system/test_cache_azureblob.py +127 -0
- mapproxy/test/system/test_cache_band_merge.py +103 -0
- mapproxy/test/system/test_cache_geopackage.py +144 -0
- mapproxy/test/system/test_cache_grid_names.py +89 -0
- mapproxy/test/system/test_cache_mbtiles.py +85 -0
- mapproxy/test/system/test_cache_s3.py +115 -0
- mapproxy/test/system/test_cache_source.py +146 -0
- mapproxy/test/system/test_combined_sources.py +335 -0
- mapproxy/test/system/test_coverage.py +140 -0
- mapproxy/test/system/test_decorate_img.py +214 -0
- mapproxy/test/system/test_demo.py +106 -0
- mapproxy/test/system/test_demo_with_extra_service.py +53 -0
- mapproxy/test/system/test_dimensions.py +278 -0
- mapproxy/test/system/test_disable_storage.py +42 -0
- mapproxy/test/system/test_formats.py +219 -0
- mapproxy/test/system/test_inspire_vs.py +173 -0
- mapproxy/test/system/test_kml.py +262 -0
- mapproxy/test/system/test_layergroups.py +160 -0
- mapproxy/test/system/test_legendgraphic.py +308 -0
- mapproxy/test/system/test_mapnik.py +161 -0
- mapproxy/test/system/test_mapserver.py +81 -0
- mapproxy/test/system/test_mixed_mode_format.py +195 -0
- mapproxy/test/system/test_multi_cache_layers.py +167 -0
- mapproxy/test/system/test_multiapp.py +92 -0
- mapproxy/test/system/test_refresh.py +207 -0
- mapproxy/test/system/test_renderd_client.py +304 -0
- mapproxy/test/system/test_response_headers.py +54 -0
- mapproxy/test/system/test_scalehints.py +140 -0
- mapproxy/test/system/test_seed.py +422 -0
- mapproxy/test/system/test_seed_only.py +93 -0
- mapproxy/test/system/test_sld.py +120 -0
- mapproxy/test/system/test_source_errors.py +377 -0
- mapproxy/test/system/test_tilesource_minmax_res.py +54 -0
- mapproxy/test/system/test_tms.py +276 -0
- mapproxy/test/system/test_tms_origin.py +46 -0
- mapproxy/test/system/test_util_conf.py +304 -0
- mapproxy/test/system/test_util_export.py +210 -0
- mapproxy/test/system/test_util_grids.py +88 -0
- mapproxy/test/system/test_util_wms_capabilities.py +182 -0
- mapproxy/test/system/test_watermark.py +91 -0
- mapproxy/test/system/test_wms.py +1611 -0
- mapproxy/test/system/test_wms_srs_extent.py +165 -0
- mapproxy/test/system/test_wms_version.py +85 -0
- mapproxy/test/system/test_wmsc.py +116 -0
- mapproxy/test/system/test_wmts.py +334 -0
- mapproxy/test/system/test_wmts_dimensions.py +206 -0
- mapproxy/test/system/test_wmts_restful.py +198 -0
- mapproxy/test/system/test_xslt_featureinfo.py +425 -0
- mapproxy/test/test_http_helper.py +219 -0
- mapproxy/test/unit/__init__.py +0 -0
- mapproxy/test/unit/epsg +2 -0
- mapproxy/test/unit/polygons/polygons.dbf +0 -0
- mapproxy/test/unit/polygons/polygons.shp +0 -0
- mapproxy/test/unit/polygons/polygons.shx +0 -0
- mapproxy/test/unit/test_async.py +245 -0
- mapproxy/test/unit/test_auth.py +419 -0
- mapproxy/test/unit/test_cache.py +1193 -0
- mapproxy/test/unit/test_cache_azureblob.py +94 -0
- mapproxy/test/unit/test_cache_compact.py +319 -0
- mapproxy/test/unit/test_cache_couchdb.py +114 -0
- mapproxy/test/unit/test_cache_geopackage.py +221 -0
- mapproxy/test/unit/test_cache_redis.py +67 -0
- mapproxy/test/unit/test_cache_riak.py +76 -0
- mapproxy/test/unit/test_cache_s3.py +84 -0
- mapproxy/test/unit/test_cache_tile.py +427 -0
- mapproxy/test/unit/test_client.py +479 -0
- mapproxy/test/unit/test_client_arcgis.py +73 -0
- mapproxy/test/unit/test_client_cgi.py +136 -0
- mapproxy/test/unit/test_collections.py +116 -0
- mapproxy/test/unit/test_concat_legends.py +37 -0
- mapproxy/test/unit/test_conf_loader.py +1061 -0
- mapproxy/test/unit/test_conf_validator.py +416 -0
- mapproxy/test/unit/test_config.py +117 -0
- mapproxy/test/unit/test_decorate_img.py +185 -0
- mapproxy/test/unit/test_exceptions.py +258 -0
- mapproxy/test/unit/test_featureinfo.py +291 -0
- mapproxy/test/unit/test_file_lock_load.py +49 -0
- mapproxy/test/unit/test_geom.py +503 -0
- mapproxy/test/unit/test_grid.py +1258 -0
- mapproxy/test/unit/test_image.py +1053 -0
- mapproxy/test/unit/test_image_mask.py +181 -0
- mapproxy/test/unit/test_image_messages.py +197 -0
- mapproxy/test/unit/test_image_options.py +160 -0
- mapproxy/test/unit/test_isodate.py +122 -0
- mapproxy/test/unit/test_multiapp.py +163 -0
- mapproxy/test/unit/test_ogr_reader.py +50 -0
- mapproxy/test/unit/test_request.py +745 -0
- mapproxy/test/unit/test_request_wmts.py +178 -0
- mapproxy/test/unit/test_response.py +79 -0
- mapproxy/test/unit/test_seed.py +365 -0
- mapproxy/test/unit/test_seed_cachelock.py +90 -0
- mapproxy/test/unit/test_srs.py +215 -0
- mapproxy/test/unit/test_tiled_source.py +122 -0
- mapproxy/test/unit/test_tilefilter.py +31 -0
- mapproxy/test/unit/test_times.py +25 -0
- mapproxy/test/unit/test_timeutils.py +50 -0
- mapproxy/test/unit/test_util_conf_utils.py +75 -0
- mapproxy/test/unit/test_utils.py +476 -0
- mapproxy/test/unit/test_wms_capabilities.py +44 -0
- mapproxy/test/unit/test_wms_layer.py +113 -0
- mapproxy/test/unit/test_yaml.py +69 -0
- mapproxy/tilefilter.py +59 -0
- mapproxy/util/__init__.py +0 -0
- mapproxy/util/async_.py +227 -0
- mapproxy/util/collections.py +132 -0
- mapproxy/util/coverage.py +329 -0
- mapproxy/util/escape.py +10 -0
- mapproxy/util/ext/__init__.py +14 -0
- mapproxy/util/ext/dictspec/__init__.py +1 -0
- mapproxy/util/ext/dictspec/spec.py +124 -0
- mapproxy/util/ext/dictspec/test/__init__.py +0 -0
- mapproxy/util/ext/dictspec/test/test_validator.py +274 -0
- mapproxy/util/ext/dictspec/validator.py +189 -0
- mapproxy/util/ext/local.py +196 -0
- mapproxy/util/ext/lockfile.py +138 -0
- mapproxy/util/ext/odict.py +330 -0
- mapproxy/util/ext/serving.py +508 -0
- mapproxy/util/ext/tempita/__init__.py +1174 -0
- mapproxy/util/ext/tempita/_looper.py +163 -0
- mapproxy/util/ext/tempita/compat3.py +46 -0
- mapproxy/util/ext/wmsparse/__init__.py +3 -0
- mapproxy/util/ext/wmsparse/duration.py +597 -0
- mapproxy/util/ext/wmsparse/parse.py +305 -0
- mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
- mapproxy/util/ext/wmsparse/test/test_parse.py +162 -0
- mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
- mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
- mapproxy/util/ext/wmsparse/test/wms-omniscale-111.xml +90 -0
- mapproxy/util/ext/wmsparse/test/wms-omniscale-130.xml +120 -0
- mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
- mapproxy/util/ext/wmsparse/util.py +187 -0
- mapproxy/util/fs.py +156 -0
- mapproxy/util/geom.py +295 -0
- mapproxy/util/lib.py +115 -0
- mapproxy/util/lock.py +163 -0
- mapproxy/util/ogr.py +231 -0
- mapproxy/util/py.py +81 -0
- mapproxy/util/times.py +75 -0
- mapproxy/util/yaml.py +56 -0
- mapproxy/version.py +31 -0
- mapproxy/wsgiapp.py +164 -0
- mapproxy-1.16.1.dist-info/METADATA +151 -0
- mapproxy-1.16.1.dist-info/RECORD +458 -0
- mapproxy-1.16.1.dist-info/WHEEL +5 -0
- mapproxy-1.16.1.dist-info/entry_points.txt +3 -0
- mapproxy-1.16.1.dist-info/licenses/AUTHORS.txt +33 -0
- mapproxy-1.16.1.dist-info/licenses/COPYING.txt +60 -0
- mapproxy-1.16.1.dist-info/licenses/LICENSE.txt +202 -0
- mapproxy-1.16.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# -:- encoding: utf8 -:-
|
|
2
|
+
# Copyright (c) 2011, Oliver Tonnhofer <olt@omniscale.de>
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
20
|
+
# THE SOFTWARE.
|
|
21
|
+
|
|
22
|
+
from __future__ import absolute_import
|
|
23
|
+
|
|
24
|
+
import unittest
|
|
25
|
+
|
|
26
|
+
from ..validator import validate, ValidationError, SpecError
|
|
27
|
+
from ..spec import required, one_of, number, recursive, type_spec, anything
|
|
28
|
+
from mapproxy.compat import string_type
|
|
29
|
+
|
|
30
|
+
def raises(exception):
|
|
31
|
+
def wrapper(f):
|
|
32
|
+
def _wrapper(self):
|
|
33
|
+
try:
|
|
34
|
+
f(self)
|
|
35
|
+
except exception:
|
|
36
|
+
pass
|
|
37
|
+
else:
|
|
38
|
+
raise AssertionError('expected exception %s', exception)
|
|
39
|
+
return wrapper
|
|
40
|
+
|
|
41
|
+
class TestSimpleDict(unittest.TestCase):
|
|
42
|
+
def test_validate_simple_dict(self):
|
|
43
|
+
spec = {'hello': 1, 'world': True}
|
|
44
|
+
validate(spec, {'hello': 34, 'world': False})
|
|
45
|
+
|
|
46
|
+
@raises(ValidationError)
|
|
47
|
+
def test_invalid_key(self):
|
|
48
|
+
spec = {'world': True}
|
|
49
|
+
validate(spec, {'world_foo': False})
|
|
50
|
+
|
|
51
|
+
def test_empty_data(self):
|
|
52
|
+
spec = {'world': 1}
|
|
53
|
+
validate(spec, {})
|
|
54
|
+
|
|
55
|
+
@raises(ValidationError)
|
|
56
|
+
def test_invalid_value(self):
|
|
57
|
+
spec = {'world': 1}
|
|
58
|
+
validate(spec, {'world_foo': False})
|
|
59
|
+
|
|
60
|
+
@raises(ValidationError)
|
|
61
|
+
def test_missing_required_key(self):
|
|
62
|
+
spec = {required('world'): 1}
|
|
63
|
+
validate(spec, {})
|
|
64
|
+
|
|
65
|
+
def test_valid_one_of(self):
|
|
66
|
+
spec = {'hello': one_of(1, bool())}
|
|
67
|
+
validate(spec, {'hello': 129})
|
|
68
|
+
validate(spec, {'hello': True})
|
|
69
|
+
|
|
70
|
+
@raises(ValidationError)
|
|
71
|
+
def test_invalid_one_of(self):
|
|
72
|
+
spec = {'hello': one_of(1, False)}
|
|
73
|
+
validate(spec, {'hello': []})
|
|
74
|
+
|
|
75
|
+
def test_instances_and_types(self):
|
|
76
|
+
spec = {'str()': str(), 'string_type': string_type, 'int': int, 'int()': int()}
|
|
77
|
+
validate(spec, {'str()': 'str', 'string_type': u'☃', 'int': 1, 'int()': 1})
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class TestLists(unittest.TestCase):
|
|
81
|
+
def test_list(self):
|
|
82
|
+
spec = [1]
|
|
83
|
+
validate(spec, [1, 2, 3, 4, -9])
|
|
84
|
+
|
|
85
|
+
def test_empty_list(self):
|
|
86
|
+
spec = [1]
|
|
87
|
+
validate(spec, [])
|
|
88
|
+
|
|
89
|
+
@raises(ValidationError)
|
|
90
|
+
def test_invalid_item(self):
|
|
91
|
+
spec = [1]
|
|
92
|
+
validate(spec, [1, 'hello'])
|
|
93
|
+
|
|
94
|
+
class TestNumber(unittest.TestCase):
|
|
95
|
+
def check_valid(self, spec, data):
|
|
96
|
+
validate(spec, data)
|
|
97
|
+
|
|
98
|
+
def test_numbers(self):
|
|
99
|
+
spec = number()
|
|
100
|
+
for i in (0, 1, 23e999, int(10e20), 23.1, -0.0000000001):
|
|
101
|
+
self.check_valid(spec, i)
|
|
102
|
+
|
|
103
|
+
class TestNested(unittest.TestCase):
|
|
104
|
+
def check_valid(self, spec, data):
|
|
105
|
+
validate(spec, data)
|
|
106
|
+
|
|
107
|
+
def check_invalid(self, spec, data):
|
|
108
|
+
try:
|
|
109
|
+
validate(spec, data)
|
|
110
|
+
except ValidationError:
|
|
111
|
+
pass
|
|
112
|
+
else:
|
|
113
|
+
assert False, "expected ValidationError"
|
|
114
|
+
|
|
115
|
+
def test_dict(self):
|
|
116
|
+
spec = {
|
|
117
|
+
'globals': {
|
|
118
|
+
'image': {
|
|
119
|
+
'format': {
|
|
120
|
+
'png': {
|
|
121
|
+
'mode': 'RGB',
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
'cache': {
|
|
126
|
+
'base_dir': '/path/to/foo'
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
self.check_valid(spec, {'globals': {'image': {'format': {'png': {'mode': 'P'}}}}})
|
|
132
|
+
self.check_valid(spec, {'globals': {'image': {'format': {'png': {'mode': 'P'}}},
|
|
133
|
+
'cache': {'base_dir': '/somewhere'}}})
|
|
134
|
+
self.check_invalid(spec, {'globals': {'image': {'foo': {'png': {'mode': 'P'}}}}})
|
|
135
|
+
self.check_invalid(spec, {'globals': {'image': {'png': {'png': {'mode': 1}}}}})
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_errors_in_unicode_keys(self):
|
|
139
|
+
# should not raise UnicodeEncodeError
|
|
140
|
+
spec = {
|
|
141
|
+
anything(): str(),
|
|
142
|
+
}
|
|
143
|
+
self.check_invalid(spec, {u'globalü': 12})
|
|
144
|
+
|
|
145
|
+
class TestRecursive(unittest.TestCase):
|
|
146
|
+
def test(self):
|
|
147
|
+
spec = recursive({'hello': str(), 'more': recursive()})
|
|
148
|
+
validate(spec, {'hello': 'world', 'more': {'hello': 'foo', 'more': {'more': {}}}})
|
|
149
|
+
|
|
150
|
+
def test_multiple(self):
|
|
151
|
+
spec = {'a': recursive({'hello': str(), 'more': recursive()}), 'b': recursive({'foo': recursive()})}
|
|
152
|
+
validate(spec, {'b': {'foo': {'foo': {}}}})
|
|
153
|
+
validate(spec, {'a': {'hello': 'world', 'more': {'hello': 'foo', 'more': {'more': {}}}}})
|
|
154
|
+
validate(spec, {'b': {'foo': {'foo': {}}},
|
|
155
|
+
'a': {'hello': 'world', 'more': {'hello': 'foo', 'more': {'more': {}}}}})
|
|
156
|
+
@raises(SpecError)
|
|
157
|
+
def test_without_spec(self):
|
|
158
|
+
spec = {'a': recursive()}
|
|
159
|
+
validate(spec, {'a': {'a': {}}})
|
|
160
|
+
|
|
161
|
+
class TestTypeSpec(unittest.TestCase):
|
|
162
|
+
def test(self):
|
|
163
|
+
spec = type_spec('type', {'foo': {'alpha': str()}, 'bar': {'one': 1, 'two': str()}})
|
|
164
|
+
validate(spec, {'type': 'foo', 'alpha': 'yes'})
|
|
165
|
+
validate(spec, {'type': 'bar', 'one': 2})
|
|
166
|
+
|
|
167
|
+
def test_missing_type(self):
|
|
168
|
+
spec = type_spec('type', {'foo': {'alpha': str()}, 'bar': {'one': 1, 'two': str()}})
|
|
169
|
+
try:
|
|
170
|
+
validate(spec, {'alpha': 'yes'})
|
|
171
|
+
except ValidationError as ex:
|
|
172
|
+
assert "'type' not in ." in ex.errors[0]
|
|
173
|
+
else:
|
|
174
|
+
assert False
|
|
175
|
+
|
|
176
|
+
def test_unknown_type(self):
|
|
177
|
+
spec = type_spec('type', {'foo': {'alpha': str()}, 'bar': {'one': 1, 'two': str()}})
|
|
178
|
+
try:
|
|
179
|
+
validate(spec, {'type': 'baz', 'alpha': 'yes'})
|
|
180
|
+
except ValidationError as ex:
|
|
181
|
+
assert "unknown type value 'baz' in ." in ex.errors[0], ex
|
|
182
|
+
else:
|
|
183
|
+
assert False
|
|
184
|
+
|
|
185
|
+
def test_no_type_dict(self):
|
|
186
|
+
spec = {'dict': type_spec('type', {'foo': {'alpha': str()}, 'bar': {'one': 1, 'two': str()}})}
|
|
187
|
+
try:
|
|
188
|
+
validate(spec, {'dict': None})
|
|
189
|
+
except ValidationError as ex:
|
|
190
|
+
assert "dict is empty" in ex.errors[0], ex
|
|
191
|
+
else:
|
|
192
|
+
assert False
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class TestErrors(unittest.TestCase):
|
|
196
|
+
def test_invalid_types(self):
|
|
197
|
+
spec = {'str': str, 'str()': str(), 'string_type': string_type, '1': 1, 'int': int}
|
|
198
|
+
try:
|
|
199
|
+
validate(spec, {'str': 1, 'str()': 1, 'string_type': 1, '1': 'a', 'int': 'int'})
|
|
200
|
+
except ValidationError as ex:
|
|
201
|
+
ex.errors.sort()
|
|
202
|
+
assert ex.errors[0] == "'a' in 1 not of type int"
|
|
203
|
+
assert ex.errors[1] == "'int' in int not of type int"
|
|
204
|
+
assert ex.errors[2] == '1 in str not of type str'
|
|
205
|
+
assert ex.errors[3] == '1 in str() not of type str'
|
|
206
|
+
assert ex.errors[4] in (
|
|
207
|
+
'1 in string_type not of type basestring', #PY2
|
|
208
|
+
'1 in string_type not of type str') #PY3
|
|
209
|
+
else:
|
|
210
|
+
assert False
|
|
211
|
+
|
|
212
|
+
def test_invalid_key(self):
|
|
213
|
+
spec = {'world': {'europe': {}}}
|
|
214
|
+
try:
|
|
215
|
+
validate(spec, {'world': {'europe': {'germany': 1}}})
|
|
216
|
+
except ValidationError as ex:
|
|
217
|
+
assert 'world.europe' in str(ex)
|
|
218
|
+
else:
|
|
219
|
+
assert False
|
|
220
|
+
|
|
221
|
+
def test_invalid_list_item(self):
|
|
222
|
+
spec = {'numbers': [number()]}
|
|
223
|
+
try:
|
|
224
|
+
validate(spec, {'numbers': [1, 2, 3, 'foo']})
|
|
225
|
+
except ValidationError as ex:
|
|
226
|
+
assert 'numbers[3] not of type number' in str(ex), str(ex)
|
|
227
|
+
else:
|
|
228
|
+
assert False
|
|
229
|
+
|
|
230
|
+
def test_multiple_invalid_list_items(self):
|
|
231
|
+
spec = {'numbers': [number()]}
|
|
232
|
+
try:
|
|
233
|
+
validate(spec, {'numbers': [1, True, 3, 'foo']})
|
|
234
|
+
except ValidationError as ex:
|
|
235
|
+
assert '2 validation errors' in str(ex), str(ex)
|
|
236
|
+
assert 'numbers[1] not of type number' in ex.errors[0]
|
|
237
|
+
assert 'numbers[3] not of type number' in ex.errors[1]
|
|
238
|
+
else:
|
|
239
|
+
assert False
|
|
240
|
+
|
|
241
|
+
def test_error_in_non_string_key(self):
|
|
242
|
+
spec = {1: bool()}
|
|
243
|
+
try:
|
|
244
|
+
validate(spec, {1: 'not a bool'})
|
|
245
|
+
except ValidationError as ex:
|
|
246
|
+
assert "'not a bool' in 1 not of type bool" in ex.errors[0]
|
|
247
|
+
else:
|
|
248
|
+
assert False
|
|
249
|
+
|
|
250
|
+
def test_error_in_non_string_key_with_anything_key_spec(self):
|
|
251
|
+
spec = {anything(): bool()}
|
|
252
|
+
try:
|
|
253
|
+
validate(spec, {1: 'not a bool'})
|
|
254
|
+
except ValidationError as ex:
|
|
255
|
+
assert "'not a bool' in 1 not of type bool" in ex.errors[0]
|
|
256
|
+
else:
|
|
257
|
+
assert False
|
|
258
|
+
|
|
259
|
+
def test_one_of_with_custom_types():
|
|
260
|
+
# test for fixed validation of one_of specs with values that are
|
|
261
|
+
# not lists or dicts (e.g. recursive)
|
|
262
|
+
spec = one_of([str], recursive({required('foo'): string_type}))
|
|
263
|
+
validate(spec, ['foo', 'bar'])
|
|
264
|
+
validate(spec, {'foo': 'bar'})
|
|
265
|
+
try:
|
|
266
|
+
validate(spec, {'nofoo': 'bar'})
|
|
267
|
+
except ValidationError as ex:
|
|
268
|
+
assert "missing 'foo'" in ex.errors[0]
|
|
269
|
+
else:
|
|
270
|
+
assert False
|
|
271
|
+
|
|
272
|
+
if __name__ == '__main__':
|
|
273
|
+
unittest.main()
|
|
274
|
+
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Copyright (c) 2011, Oliver Tonnhofer <olt@omniscale.de>
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
|
11
|
+
# all copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
# THE SOFTWARE.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
import re
|
|
23
|
+
from contextlib import contextmanager
|
|
24
|
+
|
|
25
|
+
from .spec import required, one_of, anything, recursive
|
|
26
|
+
from mapproxy.compat import iteritems, iterkeys, text_type
|
|
27
|
+
|
|
28
|
+
class Context(object):
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self.recurse_spec = None
|
|
31
|
+
self.obj_pos = []
|
|
32
|
+
|
|
33
|
+
def push(self, spec):
|
|
34
|
+
self.obj_pos.append(spec)
|
|
35
|
+
|
|
36
|
+
def pop(self):
|
|
37
|
+
return self.obj_pos.pop()
|
|
38
|
+
|
|
39
|
+
@contextmanager
|
|
40
|
+
def pos(self, spec):
|
|
41
|
+
self.push(spec)
|
|
42
|
+
yield
|
|
43
|
+
self.pop()
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def current_pos(self):
|
|
47
|
+
return ''.join(self.obj_pos).lstrip('.') or '.'
|
|
48
|
+
|
|
49
|
+
def validate(spec, data):
|
|
50
|
+
"""
|
|
51
|
+
Validate `data` against `spec`.
|
|
52
|
+
"""
|
|
53
|
+
return Validator(spec).validate(data)
|
|
54
|
+
|
|
55
|
+
class ValidationError(TypeError):
|
|
56
|
+
def __init__(self, msg, errors=None, informal_only=False):
|
|
57
|
+
TypeError.__init__(self, msg)
|
|
58
|
+
self.informal_only = informal_only
|
|
59
|
+
self.errors = errors or []
|
|
60
|
+
|
|
61
|
+
class SpecError(TypeError):
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
class Validator(object):
|
|
65
|
+
def __init__(self, spec, fail_fast=False):
|
|
66
|
+
"""
|
|
67
|
+
:params fail_fast: True if it should raise on the first error
|
|
68
|
+
"""
|
|
69
|
+
self.context = Context()
|
|
70
|
+
self.complete_spec = spec
|
|
71
|
+
self.raise_first_error = fail_fast
|
|
72
|
+
self.errors = False
|
|
73
|
+
self.messages = []
|
|
74
|
+
|
|
75
|
+
def validate(self, data):
|
|
76
|
+
self._validate_part(self.complete_spec, data)
|
|
77
|
+
|
|
78
|
+
if self.messages:
|
|
79
|
+
if len(self.messages) == 1:
|
|
80
|
+
raise ValidationError(self.messages[0], self.messages, informal_only=not self.errors)
|
|
81
|
+
else:
|
|
82
|
+
raise ValidationError('found %d validation errors.' % len(self.messages), self.messages,
|
|
83
|
+
informal_only=not self.errors)
|
|
84
|
+
|
|
85
|
+
def _validate_part(self, spec, data):
|
|
86
|
+
if hasattr(spec, 'subspec'):
|
|
87
|
+
try:
|
|
88
|
+
spec = spec.subspec(data, self.context)
|
|
89
|
+
except ValueError as ex:
|
|
90
|
+
return self._handle_error(str(ex))
|
|
91
|
+
|
|
92
|
+
if isinstance(spec, recursive):
|
|
93
|
+
if spec.spec:
|
|
94
|
+
self.context.recurse_spec = spec.spec
|
|
95
|
+
self._validate_part(spec.spec, data)
|
|
96
|
+
self.context.recurse_spec = None
|
|
97
|
+
return
|
|
98
|
+
else:
|
|
99
|
+
spec = self.context.recurse_spec
|
|
100
|
+
if spec is None:
|
|
101
|
+
raise SpecError('found recursive() outside recursive spec')
|
|
102
|
+
|
|
103
|
+
if isinstance(spec, anything):
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
if data is None:
|
|
107
|
+
data = {}
|
|
108
|
+
|
|
109
|
+
if isinstance(spec, one_of):
|
|
110
|
+
# check if at least one spec type matches
|
|
111
|
+
for subspec in spec.specs:
|
|
112
|
+
if type_matches(subspec, data):
|
|
113
|
+
self._validate_part(subspec, data)
|
|
114
|
+
return
|
|
115
|
+
else:
|
|
116
|
+
return self._handle_error("%r in %s not of any type %s" %
|
|
117
|
+
(data, self.context.current_pos, ', '.join(map(type_str, spec.specs))))
|
|
118
|
+
elif not type_matches(spec, data):
|
|
119
|
+
return self._handle_error("%r in %s not of type %s" %
|
|
120
|
+
(data, self.context.current_pos, type_str(spec)))
|
|
121
|
+
|
|
122
|
+
# recurse in dicts and lists
|
|
123
|
+
if isinstance(spec, dict):
|
|
124
|
+
self._validate_dict(spec, data)
|
|
125
|
+
elif isinstance(spec, list):
|
|
126
|
+
self._validate_list(spec, data)
|
|
127
|
+
|
|
128
|
+
def _validate_dict(self, spec, data):
|
|
129
|
+
accept_any_key = False
|
|
130
|
+
any_key_spec = None
|
|
131
|
+
for k in iterkeys(spec):
|
|
132
|
+
if isinstance(k, required):
|
|
133
|
+
if k not in data:
|
|
134
|
+
self._handle_error("missing '%s', not in %s" %
|
|
135
|
+
(k, self.context.current_pos))
|
|
136
|
+
if isinstance(k, anything):
|
|
137
|
+
accept_any_key = True
|
|
138
|
+
any_key_spec = spec[k]
|
|
139
|
+
|
|
140
|
+
for k, v in iteritems(data):
|
|
141
|
+
if accept_any_key:
|
|
142
|
+
with self.context.pos('.' + text_type(k)):
|
|
143
|
+
self._validate_part(any_key_spec, v)
|
|
144
|
+
|
|
145
|
+
else:
|
|
146
|
+
if k not in spec:
|
|
147
|
+
self._handle_error("unknown '%s' in %s" %
|
|
148
|
+
(k, self.context.current_pos), info_only=True)
|
|
149
|
+
continue
|
|
150
|
+
with self.context.pos('.' + text_type(k)):
|
|
151
|
+
self._validate_part(spec[k], v)
|
|
152
|
+
|
|
153
|
+
def _validate_list(self, spec, data):
|
|
154
|
+
if not len(spec) == 1:
|
|
155
|
+
raise SpecError('lists support only one type, got: %s' % spec)
|
|
156
|
+
for i, v in enumerate(data):
|
|
157
|
+
with self.context.pos('[%d]' % i):
|
|
158
|
+
self._validate_part(spec[0], v)
|
|
159
|
+
|
|
160
|
+
def _handle_error(self, msg, info_only=False):
|
|
161
|
+
if not info_only:
|
|
162
|
+
self.errors = True
|
|
163
|
+
if self.raise_first_error and not info_only:
|
|
164
|
+
raise ValidationError(msg)
|
|
165
|
+
self.messages.append(msg)
|
|
166
|
+
|
|
167
|
+
def type_str(spec):
|
|
168
|
+
if not isinstance(spec, type):
|
|
169
|
+
spec = type(spec)
|
|
170
|
+
|
|
171
|
+
match = re.match(r"<type '(\w+)'>", str(spec))
|
|
172
|
+
if match:
|
|
173
|
+
return match.group(1)
|
|
174
|
+
|
|
175
|
+
match = re.match(r"<class '([\w._]+)'>", str(spec))
|
|
176
|
+
if match:
|
|
177
|
+
return match.group(1).split('.')[-1]
|
|
178
|
+
|
|
179
|
+
return str(type)
|
|
180
|
+
|
|
181
|
+
def type_matches(spec, data):
|
|
182
|
+
if hasattr(spec, 'compare_type'):
|
|
183
|
+
return spec.compare_type(data)
|
|
184
|
+
if isinstance(spec, type):
|
|
185
|
+
spec_type = spec
|
|
186
|
+
else:
|
|
187
|
+
spec_type = type(spec)
|
|
188
|
+
return isinstance(data, spec_type)
|
|
189
|
+
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
This module implements context-local objects.
|
|
4
|
+
|
|
5
|
+
This is a partial version of werkzeug/local.py containing only Local and
|
|
6
|
+
StackLocal.
|
|
7
|
+
|
|
8
|
+
Last update: 2011-03-15 9ada59c958b2edbb9739fb55a6b32ef4a97dac07
|
|
9
|
+
|
|
10
|
+
:copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
|
|
11
|
+
:license: BSD, see LICENSE for more details.
|
|
12
|
+
"""
|
|
13
|
+
try:
|
|
14
|
+
from greenlet import getcurrent as get_current_greenlet
|
|
15
|
+
except ImportError: # pragma: no cover
|
|
16
|
+
try:
|
|
17
|
+
from py.magic import greenlet
|
|
18
|
+
get_current_greenlet = greenlet.getcurrent
|
|
19
|
+
del greenlet
|
|
20
|
+
except Exception:
|
|
21
|
+
# catch all, py.* fails with so many different errors.
|
|
22
|
+
get_current_greenlet = int
|
|
23
|
+
try:
|
|
24
|
+
from _thread import get_ident as get_current_thread, allocate_lock
|
|
25
|
+
except ImportError: # pragma: no cover
|
|
26
|
+
try:
|
|
27
|
+
from thread import get_ident as get_current_thread, allocate_lock
|
|
28
|
+
except ImportError: # pragma: no cover
|
|
29
|
+
from dummy_thread import get_ident as get_current_thread, allocate_lock
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# get the best ident function. if greenlets are not installed we can
|
|
33
|
+
# safely just use the builtin thread function and save a python methodcall
|
|
34
|
+
# and the cost of calculating a hash.
|
|
35
|
+
if get_current_greenlet is int: # pragma: no cover
|
|
36
|
+
get_ident = get_current_thread
|
|
37
|
+
else:
|
|
38
|
+
get_ident = lambda: (get_current_thread(), get_current_greenlet())
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def release_local(local):
|
|
42
|
+
"""Releases the contents of the local for the current context.
|
|
43
|
+
This makes it possible to use locals without a manager.
|
|
44
|
+
|
|
45
|
+
Example::
|
|
46
|
+
|
|
47
|
+
>>> loc = Local()
|
|
48
|
+
>>> loc.foo = 42
|
|
49
|
+
>>> release_local(loc)
|
|
50
|
+
>>> hasattr(loc, 'foo')
|
|
51
|
+
False
|
|
52
|
+
|
|
53
|
+
With this function one can release :class:`Local` objects as well
|
|
54
|
+
as :class:`StackLocal` objects. However it is not possible to
|
|
55
|
+
release data held by proxies that way, one always has to retain
|
|
56
|
+
a reference to the underlying local object in order to be able
|
|
57
|
+
to release it.
|
|
58
|
+
|
|
59
|
+
.. versionadded:: 0.6.1
|
|
60
|
+
"""
|
|
61
|
+
local.__release_local__()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class Local(object):
|
|
65
|
+
__slots__ = ('__storage__', '__lock__', '__ident_func__')
|
|
66
|
+
|
|
67
|
+
def __init__(self):
|
|
68
|
+
object.__setattr__(self, '__storage__', {})
|
|
69
|
+
object.__setattr__(self, '__lock__', allocate_lock())
|
|
70
|
+
object.__setattr__(self, '__ident_func__', get_ident)
|
|
71
|
+
|
|
72
|
+
def __iter__(self):
|
|
73
|
+
return self.__storage__.iteritems()
|
|
74
|
+
|
|
75
|
+
def __call__(self, proxy):
|
|
76
|
+
"""Create a proxy for a name."""
|
|
77
|
+
return LocalProxy(self, proxy)
|
|
78
|
+
|
|
79
|
+
def __release_local__(self):
|
|
80
|
+
self.__storage__.pop(self.__ident_func__(), None)
|
|
81
|
+
|
|
82
|
+
def __getattr__(self, name):
|
|
83
|
+
try:
|
|
84
|
+
return self.__storage__[self.__ident_func__()][name]
|
|
85
|
+
except KeyError:
|
|
86
|
+
raise AttributeError(name)
|
|
87
|
+
|
|
88
|
+
def __setattr__(self, name, value):
|
|
89
|
+
ident = self.__ident_func__()
|
|
90
|
+
self.__lock__.acquire()
|
|
91
|
+
try:
|
|
92
|
+
storage = self.__storage__
|
|
93
|
+
if ident in storage:
|
|
94
|
+
storage[ident][name] = value
|
|
95
|
+
else:
|
|
96
|
+
storage[ident] = {name: value}
|
|
97
|
+
finally:
|
|
98
|
+
self.__lock__.release()
|
|
99
|
+
|
|
100
|
+
def __delattr__(self, name):
|
|
101
|
+
try:
|
|
102
|
+
del self.__storage__[self.__ident_func__()][name]
|
|
103
|
+
except KeyError:
|
|
104
|
+
raise AttributeError(name)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class LocalStack(object):
|
|
108
|
+
"""This class works similar to a :class:`Local` but keeps a stack
|
|
109
|
+
of objects instead. This is best explained with an example::
|
|
110
|
+
|
|
111
|
+
>>> ls = LocalStack()
|
|
112
|
+
>>> ls.push(42)
|
|
113
|
+
[42]
|
|
114
|
+
>>> ls.top
|
|
115
|
+
42
|
|
116
|
+
>>> ls.push(23)
|
|
117
|
+
[42, 23]
|
|
118
|
+
>>> ls.top
|
|
119
|
+
23
|
|
120
|
+
>>> ls.pop()
|
|
121
|
+
23
|
|
122
|
+
>>> ls.top
|
|
123
|
+
42
|
|
124
|
+
|
|
125
|
+
They can be force released by using a :class:`LocalManager` or with
|
|
126
|
+
the :func:`release_local` function but the correct way is to pop the
|
|
127
|
+
item from the stack after using. When the stack is empty it will
|
|
128
|
+
no longer be bound to the current context (and as such released).
|
|
129
|
+
|
|
130
|
+
By calling the stack without arguments it returns a proxy that resolves to
|
|
131
|
+
the topmost item on the stack.
|
|
132
|
+
|
|
133
|
+
.. versionadded:: 0.6.1
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
def __init__(self):
|
|
137
|
+
self._local = Local()
|
|
138
|
+
self._lock = allocate_lock()
|
|
139
|
+
|
|
140
|
+
def __release_local__(self):
|
|
141
|
+
self._local.__release_local__()
|
|
142
|
+
|
|
143
|
+
def _get__ident_func__(self):
|
|
144
|
+
return self._local.__ident_func__
|
|
145
|
+
def _set__ident_func__(self, value):
|
|
146
|
+
object.__setattr__(self._local, '__ident_func__', value)
|
|
147
|
+
__ident_func__ = property(_get__ident_func__, _set__ident_func__)
|
|
148
|
+
del _get__ident_func__, _set__ident_func__
|
|
149
|
+
|
|
150
|
+
def __call__(self):
|
|
151
|
+
def _lookup():
|
|
152
|
+
rv = self.top
|
|
153
|
+
if rv is None:
|
|
154
|
+
raise RuntimeError('object unbound')
|
|
155
|
+
return rv
|
|
156
|
+
return LocalProxy(_lookup)
|
|
157
|
+
|
|
158
|
+
def push(self, obj):
|
|
159
|
+
"""Pushes a new item to the stack"""
|
|
160
|
+
self._lock.acquire()
|
|
161
|
+
try:
|
|
162
|
+
rv = getattr(self._local, 'stack', None)
|
|
163
|
+
if rv is None:
|
|
164
|
+
self._local.stack = rv = []
|
|
165
|
+
rv.append(obj)
|
|
166
|
+
return rv
|
|
167
|
+
finally:
|
|
168
|
+
self._lock.release()
|
|
169
|
+
|
|
170
|
+
def pop(self):
|
|
171
|
+
"""Removes the topmost item from the stack, will return the
|
|
172
|
+
old value or `None` if the stack was already empty.
|
|
173
|
+
"""
|
|
174
|
+
self._lock.acquire()
|
|
175
|
+
try:
|
|
176
|
+
stack = getattr(self._local, 'stack', None)
|
|
177
|
+
if stack is None:
|
|
178
|
+
return None
|
|
179
|
+
elif len(stack) == 1:
|
|
180
|
+
release_local(self._local)
|
|
181
|
+
return stack[-1]
|
|
182
|
+
else:
|
|
183
|
+
return stack.pop()
|
|
184
|
+
finally:
|
|
185
|
+
self._lock.release()
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def top(self):
|
|
189
|
+
"""The topmost item on the stack. If the stack is empty,
|
|
190
|
+
`None` is returned.
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
return self._local.stack[-1]
|
|
194
|
+
except (AttributeError, IndexError):
|
|
195
|
+
return None
|
|
196
|
+
|