MapProxy 2.1.0__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-2.1.0.dist-info/AUTHORS.txt +33 -0
- MapProxy-2.1.0.dist-info/COPYING.txt +60 -0
- MapProxy-2.1.0.dist-info/LICENSE.txt +202 -0
- MapProxy-2.1.0.dist-info/METADATA +165 -0
- MapProxy-2.1.0.dist-info/RECORD +459 -0
- MapProxy-2.1.0.dist-info/WHEEL +5 -0
- MapProxy-2.1.0.dist-info/entry_points.txt +4 -0
- MapProxy-2.1.0.dist-info/top_level.txt +1 -0
- mapproxy/__init__.py +0 -0
- mapproxy/cache/__init__.py +36 -0
- mapproxy/cache/azureblob.py +145 -0
- mapproxy/cache/base.py +120 -0
- mapproxy/cache/compact.py +665 -0
- mapproxy/cache/couchdb.py +301 -0
- mapproxy/cache/dummy.py +36 -0
- mapproxy/cache/file.py +200 -0
- mapproxy/cache/geopackage.py +647 -0
- mapproxy/cache/legend.py +87 -0
- mapproxy/cache/mbtiles.py +411 -0
- mapproxy/cache/meta.py +80 -0
- mapproxy/cache/path.py +261 -0
- mapproxy/cache/redis.py +152 -0
- mapproxy/cache/renderd.py +100 -0
- mapproxy/cache/riak.py +206 -0
- mapproxy/cache/s3.py +209 -0
- mapproxy/cache/tile.py +736 -0
- mapproxy/client/__init__.py +0 -0
- mapproxy/client/arcgis.py +82 -0
- mapproxy/client/cgi.py +141 -0
- mapproxy/client/http.py +295 -0
- mapproxy/client/log.py +33 -0
- mapproxy/client/tile.py +158 -0
- mapproxy/client/wms.py +255 -0
- mapproxy/compat/__init__.py +0 -0
- mapproxy/compat/image.py +86 -0
- mapproxy/config/__init__.py +22 -0
- mapproxy/config/config-schema.json +813 -0
- mapproxy/config/config.py +213 -0
- mapproxy/config/coverage.py +108 -0
- mapproxy/config/defaults.py +102 -0
- mapproxy/config/loader.py +2399 -0
- mapproxy/config/spec.py +657 -0
- mapproxy/config/validator.py +242 -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 +598 -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 +149 -0
- mapproxy/featureinfo.py +251 -0
- mapproxy/grid.py +1199 -0
- mapproxy/image/__init__.py +549 -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 +79 -0
- mapproxy/image/merge.py +323 -0
- mapproxy/image/message.py +357 -0
- mapproxy/image/opts.py +185 -0
- mapproxy/image/tile.py +171 -0
- mapproxy/image/transform.py +350 -0
- mapproxy/layer.py +489 -0
- mapproxy/multiapp.py +230 -0
- mapproxy/proj.py +309 -0
- mapproxy/request/__init__.py +18 -0
- mapproxy/request/arcgis.py +268 -0
- mapproxy/request/base.py +466 -0
- mapproxy/request/tile.py +131 -0
- mapproxy/request/wms/__init__.py +829 -0
- mapproxy/request/wms/exception.py +107 -0
- mapproxy/request/wmts.py +442 -0
- mapproxy/response.py +237 -0
- mapproxy/script/__init__.py +0 -0
- mapproxy/script/conf/__init__.py +0 -0
- mapproxy/script/conf/app.py +222 -0
- mapproxy/script/conf/caches.py +44 -0
- mapproxy/script/conf/geopackage.py +136 -0
- mapproxy/script/conf/layers.py +54 -0
- mapproxy/script/conf/seeds.py +36 -0
- mapproxy/script/conf/sources.py +88 -0
- mapproxy/script/conf/utils.py +148 -0
- mapproxy/script/defrag.py +187 -0
- mapproxy/script/export.py +337 -0
- mapproxy/script/grids.py +198 -0
- mapproxy/script/scales.py +134 -0
- mapproxy/script/util.py +410 -0
- mapproxy/script/wms_capabilities.py +160 -0
- mapproxy/seed/__init__.py +0 -0
- mapproxy/seed/cachelock.py +127 -0
- mapproxy/seed/cleanup.py +191 -0
- mapproxy/seed/config.py +481 -0
- mapproxy/seed/script.py +391 -0
- mapproxy/seed/seeder.py +551 -0
- mapproxy/seed/spec.py +66 -0
- mapproxy/seed/util.py +266 -0
- mapproxy/service/__init__.py +14 -0
- mapproxy/service/base.py +45 -0
- mapproxy/service/demo.py +364 -0
- mapproxy/service/kml.py +333 -0
- mapproxy/service/ows.py +39 -0
- mapproxy/service/template_helper.py +55 -0
- mapproxy/service/templates/demo/capabilities_demo.html +18 -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 +117 -0
- mapproxy/service/templates/demo/wms_demo.html +144 -0
- mapproxy/service/templates/demo/wmts_demo.html +118 -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 +540 -0
- mapproxy/service/wms.py +868 -0
- mapproxy/service/wmts.py +387 -0
- mapproxy/source/__init__.py +83 -0
- mapproxy/source/arcgis.py +39 -0
- mapproxy/source/error.py +40 -0
- mapproxy/source/mapnik.py +262 -0
- mapproxy/source/tile.py +97 -0
- mapproxy/source/wms.py +273 -0
- mapproxy/srs.py +734 -0
- mapproxy/template.py +54 -0
- mapproxy/test/__init__.py +0 -0
- mapproxy/test/conftest.py +8 -0
- mapproxy/test/helper.py +255 -0
- mapproxy/test/http.py +511 -0
- mapproxy/test/image.py +219 -0
- mapproxy/test/mocker.py +2291 -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_coverage.yaml +84 -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 +111 -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 +29 -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 +1133 -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_coverage.py +168 -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 +56 -0
- mapproxy/test/system/test_demo_with_extra_service.py +57 -0
- mapproxy/test/system/test_dimensions.py +279 -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 +264 -0
- mapproxy/test/system/test_layergroups.py +160 -0
- mapproxy/test/system/test_legendgraphic.py +308 -0
- mapproxy/test/system/test_mapnik.py +162 -0
- mapproxy/test/system/test_mapserver.py +83 -0
- mapproxy/test/system/test_mixed_mode_format.py +201 -0
- mapproxy/test/system/test_multi_cache_layers.py +169 -0
- mapproxy/test/system/test_multiapp.py +92 -0
- mapproxy/test/system/test_refresh.py +206 -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 +425 -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 +277 -0
- mapproxy/test/system/test_tms_origin.py +46 -0
- mapproxy/test/system/test_util_conf.py +434 -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 +1616 -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 +423 -0
- mapproxy/test/test_http_helper.py +217 -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 +242 -0
- mapproxy/test/unit/test_auth.py +430 -0
- mapproxy/test/unit/test_cache.py +1356 -0
- mapproxy/test/unit/test_cache_azureblob.py +97 -0
- mapproxy/test/unit/test_cache_compact.py +324 -0
- mapproxy/test/unit/test_cache_couchdb.py +118 -0
- mapproxy/test/unit/test_cache_geopackage.py +256 -0
- mapproxy/test/unit/test_cache_redis.py +123 -0
- mapproxy/test/unit/test_cache_riak.py +80 -0
- mapproxy/test/unit/test_cache_s3.py +93 -0
- mapproxy/test/unit/test_cache_tile.py +477 -0
- mapproxy/test/unit/test_client.py +488 -0
- mapproxy/test/unit/test_client_arcgis.py +74 -0
- mapproxy/test/unit/test_client_cgi.py +140 -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 +1267 -0
- mapproxy/test/unit/test_conf_validator.py +427 -0
- mapproxy/test/unit/test_config.py +118 -0
- mapproxy/test/unit/test_decorate_img.py +185 -0
- mapproxy/test/unit/test_exceptions.py +270 -0
- mapproxy/test/unit/test_featureinfo.py +313 -0
- mapproxy/test/unit/test_file_lock_load.py +49 -0
- mapproxy/test/unit/test_geom.py +512 -0
- mapproxy/test/unit/test_grid.py +1279 -0
- mapproxy/test/unit/test_image.py +1051 -0
- mapproxy/test/unit/test_image_mask.py +181 -0
- mapproxy/test/unit/test_image_messages.py +209 -0
- mapproxy/test/unit/test_image_options.py +160 -0
- mapproxy/test/unit/test_isodate.py +118 -0
- mapproxy/test/unit/test_multiapp.py +163 -0
- mapproxy/test/unit/test_ogr_reader.py +51 -0
- mapproxy/test/unit/test_request.py +745 -0
- mapproxy/test/unit/test_request_wmts.py +178 -0
- mapproxy/test/unit/test_response.py +78 -0
- mapproxy/test/unit/test_seed.py +365 -0
- mapproxy/test/unit/test_seed_cachelock.py +91 -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 +68 -0
- mapproxy/tilefilter.py +61 -0
- mapproxy/util/__init__.py +0 -0
- mapproxy/util/async_.py +229 -0
- mapproxy/util/collections.py +134 -0
- mapproxy/util/coverage.py +337 -0
- mapproxy/util/ext/__init__.py +14 -0
- mapproxy/util/ext/dictspec/__init__.py +1 -0
- mapproxy/util/ext/dictspec/spec.py +131 -0
- mapproxy/util/ext/dictspec/test/__init__.py +0 -0
- mapproxy/util/ext/dictspec/test/test_validator.py +278 -0
- mapproxy/util/ext/dictspec/validator.py +194 -0
- mapproxy/util/ext/local.py +198 -0
- mapproxy/util/ext/lockfile.py +140 -0
- mapproxy/util/ext/odict.py +321 -0
- mapproxy/util/ext/serving.py +491 -0
- mapproxy/util/ext/tempita/__init__.py +1093 -0
- mapproxy/util/ext/tempita/_looper.py +163 -0
- mapproxy/util/ext/tempita/string_utils.py +24 -0
- mapproxy/util/ext/wmsparse/__init__.py +3 -0
- mapproxy/util/ext/wmsparse/duration.py +600 -0
- mapproxy/util/ext/wmsparse/parse.py +307 -0
- mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
- mapproxy/util/ext/wmsparse/test/test_parse.py +111 -0
- mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
- mapproxy/util/ext/wmsparse/test/wms-example-111.xml +90 -0
- mapproxy/util/ext/wmsparse/test/wms-example-130.xml +120 -0
- mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
- mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
- mapproxy/util/ext/wmsparse/util.py +189 -0
- mapproxy/util/fs.py +164 -0
- mapproxy/util/geom.py +307 -0
- mapproxy/util/lib.py +117 -0
- mapproxy/util/lock.py +171 -0
- mapproxy/util/ogr.py +247 -0
- mapproxy/util/py.py +75 -0
- mapproxy/util/times.py +78 -0
- mapproxy/util/yaml.py +58 -0
- mapproxy/version.py +33 -0
- mapproxy/wsgiapp.py +167 -0
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
WSGI server code for `mapproxy-util serve-develop`.
|
|
4
|
+
Supports automatic reloading and proper ^C handling.
|
|
5
|
+
|
|
6
|
+
Stripped down version of werkzeug.serving.
|
|
7
|
+
|
|
8
|
+
:copyright: (c) 2013 by the Werkzeug Team, see AUTHORS for more details.
|
|
9
|
+
:license: BSD, see LICENSE for more details.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import socket
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
import signal
|
|
17
|
+
import subprocess
|
|
18
|
+
from itertools import chain
|
|
19
|
+
from urllib.parse import urlparse, unquote
|
|
20
|
+
|
|
21
|
+
from werkzeug.exceptions import InternalServerError
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import thread
|
|
25
|
+
except ImportError:
|
|
26
|
+
import _thread as thread
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
from SocketServer import ThreadingMixIn
|
|
30
|
+
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
|
31
|
+
except ImportError:
|
|
32
|
+
from socketserver import ThreadingMixIn
|
|
33
|
+
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
34
|
+
|
|
35
|
+
from mapproxy.util.py import reraise
|
|
36
|
+
import mapproxy.version
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def wsgi_encoding_dance(s, charset='utf-8', errors='replace'):
|
|
40
|
+
if isinstance(s, str):
|
|
41
|
+
s = s.encode(charset)
|
|
42
|
+
return s.decode('latin1', errors)
|
|
43
|
+
|
|
44
|
+
# from werkzeug.exceptions import InternalServerError, BadRequest
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _log(type, message, *args):
|
|
48
|
+
if args:
|
|
49
|
+
message = message % args
|
|
50
|
+
sys.stderr.write('[%s] %s\n' % (type, message.rstrip()))
|
|
51
|
+
sys.stderr.flush()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class WSGIRequestHandler(BaseHTTPRequestHandler, object):
|
|
55
|
+
"""A request handler that implements WSGI dispatching."""
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def server_version(self):
|
|
59
|
+
return 'MapProxy/' + mapproxy.version.__version__ + ' (Werkzeug based)'
|
|
60
|
+
|
|
61
|
+
def make_environ(self):
|
|
62
|
+
request_url = urlparse(self.path)
|
|
63
|
+
|
|
64
|
+
def shutdown_server():
|
|
65
|
+
self.server.shutdown_signal = True
|
|
66
|
+
|
|
67
|
+
url_scheme = 'http'
|
|
68
|
+
path_info = unquote(request_url.path)
|
|
69
|
+
|
|
70
|
+
environ = {
|
|
71
|
+
'wsgi.version': (1, 0),
|
|
72
|
+
'wsgi.url_scheme': url_scheme,
|
|
73
|
+
'wsgi.input': self.rfile,
|
|
74
|
+
'wsgi.errors': sys.stderr,
|
|
75
|
+
'wsgi.multithread': self.server.multithread,
|
|
76
|
+
'wsgi.multiprocess': self.server.multiprocess,
|
|
77
|
+
'wsgi.run_once': False,
|
|
78
|
+
'werkzeug.server.shutdown': shutdown_server,
|
|
79
|
+
'SERVER_SOFTWARE': self.server_version,
|
|
80
|
+
'REQUEST_METHOD': self.command,
|
|
81
|
+
'SCRIPT_NAME': '',
|
|
82
|
+
'PATH_INFO': wsgi_encoding_dance(path_info),
|
|
83
|
+
'QUERY_STRING': wsgi_encoding_dance(request_url.query),
|
|
84
|
+
'CONTENT_TYPE': self.headers.get('Content-Type', ''),
|
|
85
|
+
'CONTENT_LENGTH': self.headers.get('Content-Length', ''),
|
|
86
|
+
'REMOTE_ADDR': self.client_address[0],
|
|
87
|
+
'REMOTE_PORT': self.client_address[1],
|
|
88
|
+
'SERVER_NAME': self.server.server_address[0],
|
|
89
|
+
'SERVER_PORT': str(self.server.server_address[1]),
|
|
90
|
+
'SERVER_PROTOCOL': self.request_version
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for key, value in self.headers.items():
|
|
94
|
+
key = 'HTTP_' + key.upper().replace('-', '_')
|
|
95
|
+
if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
|
|
96
|
+
environ[key] = value
|
|
97
|
+
|
|
98
|
+
if request_url.netloc:
|
|
99
|
+
environ['HTTP_HOST'] = request_url.netloc
|
|
100
|
+
|
|
101
|
+
return environ
|
|
102
|
+
|
|
103
|
+
def run_wsgi(self):
|
|
104
|
+
if self.headers.get('Expect', '').lower().strip() == '100-continue':
|
|
105
|
+
self.wfile.write(b'HTTP/1.1 100 Continue\r\n\r\n')
|
|
106
|
+
|
|
107
|
+
environ = self.make_environ()
|
|
108
|
+
headers_set = []
|
|
109
|
+
headers_sent = []
|
|
110
|
+
|
|
111
|
+
def write(data):
|
|
112
|
+
assert headers_set, 'write() before start_response'
|
|
113
|
+
if not headers_sent:
|
|
114
|
+
status, response_headers = headers_sent[:] = headers_set
|
|
115
|
+
try:
|
|
116
|
+
code, msg = status.split(None, 1)
|
|
117
|
+
except ValueError:
|
|
118
|
+
code, msg = status, ""
|
|
119
|
+
self.send_response(int(code), msg)
|
|
120
|
+
header_keys = set()
|
|
121
|
+
for key, value in response_headers:
|
|
122
|
+
self.send_header(key, value)
|
|
123
|
+
key = key.lower()
|
|
124
|
+
header_keys.add(key)
|
|
125
|
+
if 'content-length' not in header_keys:
|
|
126
|
+
self.close_connection = True
|
|
127
|
+
self.send_header('Connection', 'close')
|
|
128
|
+
if 'server' not in header_keys:
|
|
129
|
+
self.send_header('Server', self.version_string())
|
|
130
|
+
if 'date' not in header_keys:
|
|
131
|
+
self.send_header('Date', self.date_time_string())
|
|
132
|
+
self.end_headers()
|
|
133
|
+
|
|
134
|
+
assert type(data) is bytes, 'applications must write bytes'
|
|
135
|
+
self.wfile.write(data)
|
|
136
|
+
self.wfile.flush()
|
|
137
|
+
|
|
138
|
+
def start_response(status, response_headers, exc_info=None):
|
|
139
|
+
if exc_info:
|
|
140
|
+
try:
|
|
141
|
+
if headers_sent:
|
|
142
|
+
reraise(*exc_info)
|
|
143
|
+
finally:
|
|
144
|
+
exc_info = None
|
|
145
|
+
elif headers_set:
|
|
146
|
+
raise AssertionError('Headers already set')
|
|
147
|
+
headers_set[:] = [status, response_headers]
|
|
148
|
+
return write
|
|
149
|
+
|
|
150
|
+
def execute(app):
|
|
151
|
+
application_iter = app(environ, start_response)
|
|
152
|
+
try:
|
|
153
|
+
for data in application_iter:
|
|
154
|
+
write(data)
|
|
155
|
+
if not headers_sent:
|
|
156
|
+
write(b'')
|
|
157
|
+
finally:
|
|
158
|
+
if hasattr(application_iter, 'close'):
|
|
159
|
+
application_iter.close()
|
|
160
|
+
application_iter = None
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
execute(self.server.app)
|
|
164
|
+
except (socket.error, socket.timeout) as e:
|
|
165
|
+
self.connection_dropped(e, environ)
|
|
166
|
+
except Exception:
|
|
167
|
+
if self.server.passthrough_errors:
|
|
168
|
+
raise
|
|
169
|
+
from werkzeug.debug.tbtools import get_current_traceback
|
|
170
|
+
traceback = get_current_traceback(ignore_system_exceptions=True)
|
|
171
|
+
try:
|
|
172
|
+
# if we haven't yet sent the headers but they are set
|
|
173
|
+
# we roll back to be able to set them again.
|
|
174
|
+
if not headers_sent:
|
|
175
|
+
del headers_set[:]
|
|
176
|
+
execute(InternalServerError())
|
|
177
|
+
except Exception:
|
|
178
|
+
pass
|
|
179
|
+
self.server.log('error', 'Error on request:\n%s',
|
|
180
|
+
traceback.plaintext)
|
|
181
|
+
|
|
182
|
+
def handle(self):
|
|
183
|
+
"""Handles a request ignoring dropped connections."""
|
|
184
|
+
rv = None
|
|
185
|
+
try:
|
|
186
|
+
rv = BaseHTTPRequestHandler.handle(self)
|
|
187
|
+
except (socket.error, socket.timeout) as e:
|
|
188
|
+
self.connection_dropped(e)
|
|
189
|
+
except Exception:
|
|
190
|
+
raise
|
|
191
|
+
if self.server.shutdown_signal:
|
|
192
|
+
self.initiate_shutdown()
|
|
193
|
+
return rv
|
|
194
|
+
|
|
195
|
+
def initiate_shutdown(self):
|
|
196
|
+
"""A horrible, horrible way to kill the server for Python 2.6 and
|
|
197
|
+
later. It's the best we can do.
|
|
198
|
+
"""
|
|
199
|
+
# Windows does not provide SIGKILL, go with SIGTERM then.
|
|
200
|
+
sig = getattr(signal, 'SIGKILL', signal.SIGTERM)
|
|
201
|
+
# reloader active
|
|
202
|
+
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
|
|
203
|
+
os.kill(os.getpid(), sig)
|
|
204
|
+
# python 2.7
|
|
205
|
+
self.server._BaseServer__shutdown_request = True
|
|
206
|
+
# python 2.6
|
|
207
|
+
self.server._BaseServer__serving = False
|
|
208
|
+
|
|
209
|
+
def connection_dropped(self, error, environ=None):
|
|
210
|
+
"""Called if the connection was closed by the client. By default
|
|
211
|
+
nothing happens.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
def handle_one_request(self):
|
|
215
|
+
"""Handle a single HTTP request."""
|
|
216
|
+
self.raw_requestline = self.rfile.readline()
|
|
217
|
+
if not self.raw_requestline:
|
|
218
|
+
self.close_connection = 1
|
|
219
|
+
elif self.parse_request():
|
|
220
|
+
return self.run_wsgi()
|
|
221
|
+
|
|
222
|
+
def send_response(self, code, message=None):
|
|
223
|
+
"""Send the response header and log the response code."""
|
|
224
|
+
self.log_request(code)
|
|
225
|
+
if message is None:
|
|
226
|
+
message = code in self.responses and self.responses[code][0] or ''
|
|
227
|
+
if self.request_version != 'HTTP/0.9':
|
|
228
|
+
hdr = "%s %d %s\r\n" % (self.protocol_version, code, message)
|
|
229
|
+
self.wfile.write(hdr.encode('ascii'))
|
|
230
|
+
|
|
231
|
+
def version_string(self):
|
|
232
|
+
return BaseHTTPRequestHandler.version_string(self).strip()
|
|
233
|
+
|
|
234
|
+
def address_string(self):
|
|
235
|
+
return self.client_address[0]
|
|
236
|
+
|
|
237
|
+
def log_request(self, code='-', size='-'):
|
|
238
|
+
self.log('info', '"%s" %s %s', self.requestline, code, size)
|
|
239
|
+
|
|
240
|
+
def log_error(self, *args):
|
|
241
|
+
self.log('error', *args)
|
|
242
|
+
|
|
243
|
+
def log_message(self, format, *args):
|
|
244
|
+
self.log('info', format, *args)
|
|
245
|
+
|
|
246
|
+
def log(self, type, message, *args):
|
|
247
|
+
_log(type, '%s - - [%s] %s\n' % (self.address_string(),
|
|
248
|
+
self.log_date_time_string(),
|
|
249
|
+
message % args))
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
#: backwards compatible name if someone is subclassing it
|
|
253
|
+
BaseRequestHandler = WSGIRequestHandler
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def select_ip_version(host, port):
|
|
257
|
+
"""Returns AF_INET4 or AF_INET6 depending on where to connect to."""
|
|
258
|
+
# disabled due to problems with current ipv6 implementations
|
|
259
|
+
# and various operating systems. Probably this code also is
|
|
260
|
+
# not supposed to work, but I can't come up with any other
|
|
261
|
+
# ways to implement this.
|
|
262
|
+
# try:
|
|
263
|
+
# info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
|
|
264
|
+
# socket.SOCK_STREAM, 0,
|
|
265
|
+
# socket.AI_PASSIVE)
|
|
266
|
+
# if info:
|
|
267
|
+
# return info[0][0]
|
|
268
|
+
# except socket.gaierror:
|
|
269
|
+
# pass
|
|
270
|
+
if ':' in host and hasattr(socket, 'AF_INET6'):
|
|
271
|
+
return socket.AF_INET6
|
|
272
|
+
return socket.AF_INET
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class BaseWSGIServer(HTTPServer, object):
|
|
276
|
+
"""Simple single-threaded, single-process WSGI server."""
|
|
277
|
+
multithread = False
|
|
278
|
+
multiprocess = False
|
|
279
|
+
request_queue_size = 128
|
|
280
|
+
|
|
281
|
+
def __init__(self, host, port, app, handler=None,
|
|
282
|
+
passthrough_errors=False):
|
|
283
|
+
if handler is None:
|
|
284
|
+
handler = WSGIRequestHandler
|
|
285
|
+
self.address_family = select_ip_version(host, port)
|
|
286
|
+
HTTPServer.__init__(self, (host, int(port)), handler)
|
|
287
|
+
self.app = app
|
|
288
|
+
self.passthrough_errors = passthrough_errors
|
|
289
|
+
self.shutdown_signal = False
|
|
290
|
+
|
|
291
|
+
def log(self, type, message, *args):
|
|
292
|
+
_log(type, message, *args)
|
|
293
|
+
|
|
294
|
+
def serve_forever(self):
|
|
295
|
+
self.shutdown_signal = False
|
|
296
|
+
try:
|
|
297
|
+
HTTPServer.serve_forever(self)
|
|
298
|
+
except KeyboardInterrupt:
|
|
299
|
+
pass
|
|
300
|
+
|
|
301
|
+
def handle_error(self, request, client_address):
|
|
302
|
+
if self.passthrough_errors:
|
|
303
|
+
raise
|
|
304
|
+
else:
|
|
305
|
+
return HTTPServer.handle_error(self, request, client_address)
|
|
306
|
+
|
|
307
|
+
def get_request(self):
|
|
308
|
+
con, info = self.socket.accept()
|
|
309
|
+
return con, info
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):
|
|
313
|
+
"""A WSGI server that does threading."""
|
|
314
|
+
multithread = True
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _iter_module_files():
|
|
318
|
+
# The list call is necessary on Python 3 in case the module
|
|
319
|
+
# dictionary modifies during iteration.
|
|
320
|
+
for module in list(sys.modules.values()):
|
|
321
|
+
filename = getattr(module, '__file__', None)
|
|
322
|
+
if filename:
|
|
323
|
+
old = None
|
|
324
|
+
while not os.path.isfile(filename):
|
|
325
|
+
old = filename
|
|
326
|
+
filename = os.path.dirname(filename)
|
|
327
|
+
if filename == old:
|
|
328
|
+
break
|
|
329
|
+
else:
|
|
330
|
+
if filename[-4:] in ('.pyc', '.pyo'):
|
|
331
|
+
filename = filename[:-1]
|
|
332
|
+
yield filename
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _reloader_stat_loop(extra_files=None, interval=1):
|
|
336
|
+
"""When this function is run from the main thread, it will force other
|
|
337
|
+
threads to exit when any modules currently loaded change.
|
|
338
|
+
|
|
339
|
+
Copyright notice. This function is based on the autoreload.py from
|
|
340
|
+
the CherryPy trac which originated from WSGIKit which is now dead.
|
|
341
|
+
|
|
342
|
+
:param extra_files: a list of additional files it should watch.
|
|
343
|
+
"""
|
|
344
|
+
mtimes = {}
|
|
345
|
+
while 1:
|
|
346
|
+
for filename in chain(_iter_module_files(), extra_files or ()):
|
|
347
|
+
try:
|
|
348
|
+
mtime = os.stat(filename).st_mtime
|
|
349
|
+
except OSError:
|
|
350
|
+
continue
|
|
351
|
+
|
|
352
|
+
old_time = mtimes.get(filename)
|
|
353
|
+
if old_time is None:
|
|
354
|
+
mtimes[filename] = mtime
|
|
355
|
+
continue
|
|
356
|
+
elif mtime > old_time:
|
|
357
|
+
_log('info', ' * Detected change in %r, reloading' % filename)
|
|
358
|
+
sys.exit(3)
|
|
359
|
+
time.sleep(interval)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
# currently we always use the stat loop reloader for the simple reason
|
|
363
|
+
# that the inotify one does not respond to added files properly. Also
|
|
364
|
+
# it's quite buggy and the API is a mess.
|
|
365
|
+
reloader_loop = _reloader_stat_loop
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def restart_with_reloader():
|
|
369
|
+
"""Spawn a new Python interpreter with the same arguments as this one,
|
|
370
|
+
but running the reloader thread.
|
|
371
|
+
"""
|
|
372
|
+
while 1:
|
|
373
|
+
_log('info', ' * Restarting with reloader')
|
|
374
|
+
|
|
375
|
+
args = [sys.executable] + sys.argv
|
|
376
|
+
if os.name == 'nt':
|
|
377
|
+
# pip installs commands as .exe, but sys.argv[0]
|
|
378
|
+
# can miss the prefix.
|
|
379
|
+
# Add .exe to avoid file-not-found in subprocess call.
|
|
380
|
+
# Also, recent pip versions create .exe commands that are not
|
|
381
|
+
# executable by Python, but there is a -script.py which
|
|
382
|
+
# we need to call in this case. Check for this first.
|
|
383
|
+
if os.path.exists(args[1] + '-script.py'):
|
|
384
|
+
args[1] = args[1] + '-script.py'
|
|
385
|
+
elif not args[1].endswith('.exe'):
|
|
386
|
+
args[1] = args[1] + '.exe'
|
|
387
|
+
new_environ = os.environ.copy()
|
|
388
|
+
new_environ['WERKZEUG_RUN_MAIN'] = 'true'
|
|
389
|
+
|
|
390
|
+
exit_code = subprocess.call(args, env=new_environ)
|
|
391
|
+
if exit_code != 3:
|
|
392
|
+
return exit_code
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def run_with_reloader(main_func, extra_files=None, interval=1):
|
|
396
|
+
"""Run the given function in an independent python interpreter."""
|
|
397
|
+
import signal
|
|
398
|
+
signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
|
|
399
|
+
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
|
|
400
|
+
thread.start_new_thread(main_func, ())
|
|
401
|
+
try:
|
|
402
|
+
reloader_loop(extra_files, interval)
|
|
403
|
+
except KeyboardInterrupt:
|
|
404
|
+
return
|
|
405
|
+
try:
|
|
406
|
+
sys.exit(restart_with_reloader())
|
|
407
|
+
except KeyboardInterrupt:
|
|
408
|
+
pass
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def run_simple(hostname, port, application, use_reloader=False,
|
|
412
|
+
use_debugger=False, use_evalex=True,
|
|
413
|
+
extra_files=None, reloader_interval=1, threaded=False,
|
|
414
|
+
processes=1, request_handler=None, static_files=None,
|
|
415
|
+
passthrough_errors=False):
|
|
416
|
+
"""Start an application using wsgiref and with an optional reloader. This
|
|
417
|
+
wraps `wsgiref` to fix the wrong default reporting of the multithreaded
|
|
418
|
+
WSGI variable and adds optional multithreading and fork support.
|
|
419
|
+
|
|
420
|
+
This function has a command-line interface too::
|
|
421
|
+
|
|
422
|
+
python -m werkzeug.serving --help
|
|
423
|
+
|
|
424
|
+
.. versionadded:: 0.5
|
|
425
|
+
`static_files` was added to simplify serving of static files as well
|
|
426
|
+
as `passthrough_errors`.
|
|
427
|
+
|
|
428
|
+
.. versionadded:: 0.6
|
|
429
|
+
support for SSL was added.
|
|
430
|
+
|
|
431
|
+
.. versionadded:: 0.8
|
|
432
|
+
Added support for automatically loading a SSL context from certificate
|
|
433
|
+
file and private key.
|
|
434
|
+
|
|
435
|
+
.. versionadded:: 0.9
|
|
436
|
+
Added command-line interface.
|
|
437
|
+
|
|
438
|
+
:param hostname: The host for the application. eg: ``'localhost'``
|
|
439
|
+
:param port: The port for the server. eg: ``8080``
|
|
440
|
+
:param application: the WSGI application to execute
|
|
441
|
+
:param use_reloader: should the server automatically restart the python
|
|
442
|
+
process if modules were changed?
|
|
443
|
+
:param use_debugger: should the werkzeug debugging system be used?
|
|
444
|
+
:param use_evalex: should the exception evaluation feature be enabled?
|
|
445
|
+
:param extra_files: a list of files the reloader should watch
|
|
446
|
+
additionally to the modules. For example configuration
|
|
447
|
+
files.
|
|
448
|
+
:param reloader_interval: the interval for the reloader in seconds.
|
|
449
|
+
:param threaded: should the process handle each request in a separate
|
|
450
|
+
thread?
|
|
451
|
+
:param processes: if greater than 1 then handle each request in a new process
|
|
452
|
+
up to this maximum number of concurrent processes.
|
|
453
|
+
:param request_handler: optional parameter that can be used to replace
|
|
454
|
+
the default one. You can use this to replace it
|
|
455
|
+
with a different
|
|
456
|
+
:class:`~BaseHTTPServer.BaseHTTPRequestHandler`
|
|
457
|
+
subclass.
|
|
458
|
+
:param static_files: a dict of paths for static files. This works exactly
|
|
459
|
+
like :class:`SharedDataMiddleware`, it's actually
|
|
460
|
+
just wrapping the application in that middleware before
|
|
461
|
+
serving.
|
|
462
|
+
:param passthrough_errors: set this to `True` to disable the error catching.
|
|
463
|
+
This means that the server will die on errors but
|
|
464
|
+
it can be useful to hook debuggers in (pdb etc.)
|
|
465
|
+
"""
|
|
466
|
+
if use_debugger:
|
|
467
|
+
from werkzeug.debug import DebuggedApplication
|
|
468
|
+
application = DebuggedApplication(application, use_evalex)
|
|
469
|
+
|
|
470
|
+
def inner():
|
|
471
|
+
ThreadedWSGIServer(hostname, port, application, request_handler,
|
|
472
|
+
passthrough_errors).serve_forever()
|
|
473
|
+
|
|
474
|
+
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
|
|
475
|
+
display_hostname = hostname != '*' and hostname or 'localhost'
|
|
476
|
+
if ':' in display_hostname:
|
|
477
|
+
display_hostname = '[%s]' % display_hostname
|
|
478
|
+
quit_msg = '(Press CTRL+C to quit)'
|
|
479
|
+
_log('info', ' * Running on http://%s:%d/ %s',
|
|
480
|
+
display_hostname, port, quit_msg)
|
|
481
|
+
if use_reloader:
|
|
482
|
+
# Create and destroy a socket so that any exceptions are raised before
|
|
483
|
+
# we spawn a separate Python interpreter and lose this ability.
|
|
484
|
+
address_family = select_ip_version(hostname, port)
|
|
485
|
+
test_socket = socket.socket(address_family, socket.SOCK_STREAM)
|
|
486
|
+
test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
487
|
+
test_socket.bind((hostname, port))
|
|
488
|
+
test_socket.close()
|
|
489
|
+
run_with_reloader(inner, extra_files, reloader_interval)
|
|
490
|
+
else:
|
|
491
|
+
inner()
|