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.
Files changed (459) hide show
  1. MapProxy-2.1.0.dist-info/AUTHORS.txt +33 -0
  2. MapProxy-2.1.0.dist-info/COPYING.txt +60 -0
  3. MapProxy-2.1.0.dist-info/LICENSE.txt +202 -0
  4. MapProxy-2.1.0.dist-info/METADATA +165 -0
  5. MapProxy-2.1.0.dist-info/RECORD +459 -0
  6. MapProxy-2.1.0.dist-info/WHEEL +5 -0
  7. MapProxy-2.1.0.dist-info/entry_points.txt +4 -0
  8. MapProxy-2.1.0.dist-info/top_level.txt +1 -0
  9. mapproxy/__init__.py +0 -0
  10. mapproxy/cache/__init__.py +36 -0
  11. mapproxy/cache/azureblob.py +145 -0
  12. mapproxy/cache/base.py +120 -0
  13. mapproxy/cache/compact.py +665 -0
  14. mapproxy/cache/couchdb.py +301 -0
  15. mapproxy/cache/dummy.py +36 -0
  16. mapproxy/cache/file.py +200 -0
  17. mapproxy/cache/geopackage.py +647 -0
  18. mapproxy/cache/legend.py +87 -0
  19. mapproxy/cache/mbtiles.py +411 -0
  20. mapproxy/cache/meta.py +80 -0
  21. mapproxy/cache/path.py +261 -0
  22. mapproxy/cache/redis.py +152 -0
  23. mapproxy/cache/renderd.py +100 -0
  24. mapproxy/cache/riak.py +206 -0
  25. mapproxy/cache/s3.py +209 -0
  26. mapproxy/cache/tile.py +736 -0
  27. mapproxy/client/__init__.py +0 -0
  28. mapproxy/client/arcgis.py +82 -0
  29. mapproxy/client/cgi.py +141 -0
  30. mapproxy/client/http.py +295 -0
  31. mapproxy/client/log.py +33 -0
  32. mapproxy/client/tile.py +158 -0
  33. mapproxy/client/wms.py +255 -0
  34. mapproxy/compat/__init__.py +0 -0
  35. mapproxy/compat/image.py +86 -0
  36. mapproxy/config/__init__.py +22 -0
  37. mapproxy/config/config-schema.json +813 -0
  38. mapproxy/config/config.py +213 -0
  39. mapproxy/config/coverage.py +108 -0
  40. mapproxy/config/defaults.py +102 -0
  41. mapproxy/config/loader.py +2399 -0
  42. mapproxy/config/spec.py +657 -0
  43. mapproxy/config/validator.py +242 -0
  44. mapproxy/config_template/__init__.py +0 -0
  45. mapproxy/config_template/base_config/config.wsgi +10 -0
  46. mapproxy/config_template/base_config/full_example.yaml +598 -0
  47. mapproxy/config_template/base_config/full_seed_example.yaml +79 -0
  48. mapproxy/config_template/base_config/log.ini +35 -0
  49. mapproxy/config_template/base_config/mapproxy.yaml +60 -0
  50. mapproxy/config_template/base_config/seed.yaml +27 -0
  51. mapproxy/exception.py +149 -0
  52. mapproxy/featureinfo.py +251 -0
  53. mapproxy/grid.py +1199 -0
  54. mapproxy/image/__init__.py +549 -0
  55. mapproxy/image/fonts/DejaVuSans.ttf +0 -0
  56. mapproxy/image/fonts/DejaVuSansMono.ttf +0 -0
  57. mapproxy/image/fonts/LICENSE +99 -0
  58. mapproxy/image/fonts/__init__.py +0 -0
  59. mapproxy/image/mask.py +79 -0
  60. mapproxy/image/merge.py +323 -0
  61. mapproxy/image/message.py +357 -0
  62. mapproxy/image/opts.py +185 -0
  63. mapproxy/image/tile.py +171 -0
  64. mapproxy/image/transform.py +350 -0
  65. mapproxy/layer.py +489 -0
  66. mapproxy/multiapp.py +230 -0
  67. mapproxy/proj.py +309 -0
  68. mapproxy/request/__init__.py +18 -0
  69. mapproxy/request/arcgis.py +268 -0
  70. mapproxy/request/base.py +466 -0
  71. mapproxy/request/tile.py +131 -0
  72. mapproxy/request/wms/__init__.py +829 -0
  73. mapproxy/request/wms/exception.py +107 -0
  74. mapproxy/request/wmts.py +442 -0
  75. mapproxy/response.py +237 -0
  76. mapproxy/script/__init__.py +0 -0
  77. mapproxy/script/conf/__init__.py +0 -0
  78. mapproxy/script/conf/app.py +222 -0
  79. mapproxy/script/conf/caches.py +44 -0
  80. mapproxy/script/conf/geopackage.py +136 -0
  81. mapproxy/script/conf/layers.py +54 -0
  82. mapproxy/script/conf/seeds.py +36 -0
  83. mapproxy/script/conf/sources.py +88 -0
  84. mapproxy/script/conf/utils.py +148 -0
  85. mapproxy/script/defrag.py +187 -0
  86. mapproxy/script/export.py +337 -0
  87. mapproxy/script/grids.py +198 -0
  88. mapproxy/script/scales.py +134 -0
  89. mapproxy/script/util.py +410 -0
  90. mapproxy/script/wms_capabilities.py +160 -0
  91. mapproxy/seed/__init__.py +0 -0
  92. mapproxy/seed/cachelock.py +127 -0
  93. mapproxy/seed/cleanup.py +191 -0
  94. mapproxy/seed/config.py +481 -0
  95. mapproxy/seed/script.py +391 -0
  96. mapproxy/seed/seeder.py +551 -0
  97. mapproxy/seed/spec.py +66 -0
  98. mapproxy/seed/util.py +266 -0
  99. mapproxy/service/__init__.py +14 -0
  100. mapproxy/service/base.py +45 -0
  101. mapproxy/service/demo.py +364 -0
  102. mapproxy/service/kml.py +333 -0
  103. mapproxy/service/ows.py +39 -0
  104. mapproxy/service/template_helper.py +55 -0
  105. mapproxy/service/templates/demo/capabilities_demo.html +18 -0
  106. mapproxy/service/templates/demo/demo.html +181 -0
  107. mapproxy/service/templates/demo/openlayers-demo.cfg +16 -0
  108. mapproxy/service/templates/demo/static/img/blank.gif +0 -0
  109. mapproxy/service/templates/demo/static/img/east-mini.png +0 -0
  110. mapproxy/service/templates/demo/static/img/north-mini.png +0 -0
  111. mapproxy/service/templates/demo/static/img/south-mini.png +0 -0
  112. mapproxy/service/templates/demo/static/img/west-mini.png +0 -0
  113. mapproxy/service/templates/demo/static/img/zoom-minus-mini.png +0 -0
  114. mapproxy/service/templates/demo/static/img/zoom-plus-mini.png +0 -0
  115. mapproxy/service/templates/demo/static/img/zoom-world-mini.png +0 -0
  116. mapproxy/service/templates/demo/static/logo.png +0 -0
  117. mapproxy/service/templates/demo/static/ol.css +345 -0
  118. mapproxy/service/templates/demo/static/ol.js +4 -0
  119. mapproxy/service/templates/demo/static/proj4.min.js +1 -0
  120. mapproxy/service/templates/demo/static/proj4defs.js +1 -0
  121. mapproxy/service/templates/demo/static/site.css +137 -0
  122. mapproxy/service/templates/demo/static/theme/default/framedCloud.css +0 -0
  123. mapproxy/service/templates/demo/static/theme/default/google.css +17 -0
  124. mapproxy/service/templates/demo/static/theme/default/ie6-style.css +10 -0
  125. mapproxy/service/templates/demo/static/theme/default/style.css +482 -0
  126. mapproxy/service/templates/demo/static.html +34 -0
  127. mapproxy/service/templates/demo/tms_demo.html +117 -0
  128. mapproxy/service/templates/demo/wms_demo.html +144 -0
  129. mapproxy/service/templates/demo/wmts_demo.html +118 -0
  130. mapproxy/service/templates/tms_capabilities.xml +13 -0
  131. mapproxy/service/templates/tms_exception.xml +4 -0
  132. mapproxy/service/templates/tms_root_resource.xml +7 -0
  133. mapproxy/service/templates/tms_tilemap_capabilities.xml +14 -0
  134. mapproxy/service/templates/wms100capabilities.xml +112 -0
  135. mapproxy/service/templates/wms100exception.xml +4 -0
  136. mapproxy/service/templates/wms110capabilities.xml +152 -0
  137. mapproxy/service/templates/wms110exception.xml +5 -0
  138. mapproxy/service/templates/wms111capabilities.xml +183 -0
  139. mapproxy/service/templates/wms111exception.xml +5 -0
  140. mapproxy/service/templates/wms130capabilities.xml +326 -0
  141. mapproxy/service/templates/wms130exception.xml +8 -0
  142. mapproxy/service/templates/wmts100capabilities.xml +155 -0
  143. mapproxy/service/templates/wmts100exception.xml +9 -0
  144. mapproxy/service/tile.py +540 -0
  145. mapproxy/service/wms.py +868 -0
  146. mapproxy/service/wmts.py +387 -0
  147. mapproxy/source/__init__.py +83 -0
  148. mapproxy/source/arcgis.py +39 -0
  149. mapproxy/source/error.py +40 -0
  150. mapproxy/source/mapnik.py +262 -0
  151. mapproxy/source/tile.py +97 -0
  152. mapproxy/source/wms.py +273 -0
  153. mapproxy/srs.py +734 -0
  154. mapproxy/template.py +54 -0
  155. mapproxy/test/__init__.py +0 -0
  156. mapproxy/test/conftest.py +8 -0
  157. mapproxy/test/helper.py +255 -0
  158. mapproxy/test/http.py +511 -0
  159. mapproxy/test/image.py +219 -0
  160. mapproxy/test/mocker.py +2291 -0
  161. mapproxy/test/schemas/inspire/common/1.0/common.xsd +1461 -0
  162. mapproxy/test/schemas/inspire/common/1.0/enums/enum_bul.xsd +108 -0
  163. mapproxy/test/schemas/inspire/common/1.0/enums/enum_cze.xsd +108 -0
  164. mapproxy/test/schemas/inspire/common/1.0/enums/enum_dan.xsd +108 -0
  165. mapproxy/test/schemas/inspire/common/1.0/enums/enum_dut.xsd +108 -0
  166. mapproxy/test/schemas/inspire/common/1.0/enums/enum_eng.xsd +155 -0
  167. mapproxy/test/schemas/inspire/common/1.0/enums/enum_est.xsd +108 -0
  168. mapproxy/test/schemas/inspire/common/1.0/enums/enum_fin.xsd +108 -0
  169. mapproxy/test/schemas/inspire/common/1.0/enums/enum_fre.xsd +108 -0
  170. mapproxy/test/schemas/inspire/common/1.0/enums/enum_ger.xsd +108 -0
  171. mapproxy/test/schemas/inspire/common/1.0/enums/enum_gle.xsd +109 -0
  172. mapproxy/test/schemas/inspire/common/1.0/enums/enum_gre.xsd +108 -0
  173. mapproxy/test/schemas/inspire/common/1.0/enums/enum_hun.xsd +108 -0
  174. mapproxy/test/schemas/inspire/common/1.0/enums/enum_ita.xsd +108 -0
  175. mapproxy/test/schemas/inspire/common/1.0/enums/enum_lav.xsd +108 -0
  176. mapproxy/test/schemas/inspire/common/1.0/enums/enum_lit.xsd +108 -0
  177. mapproxy/test/schemas/inspire/common/1.0/enums/enum_mlt.xsd +108 -0
  178. mapproxy/test/schemas/inspire/common/1.0/enums/enum_pol.xsd +108 -0
  179. mapproxy/test/schemas/inspire/common/1.0/enums/enum_por.xsd +108 -0
  180. mapproxy/test/schemas/inspire/common/1.0/enums/enum_rum.xsd +108 -0
  181. mapproxy/test/schemas/inspire/common/1.0/enums/enum_slo.xsd +108 -0
  182. mapproxy/test/schemas/inspire/common/1.0/enums/enum_slv.xsd +108 -0
  183. mapproxy/test/schemas/inspire/common/1.0/enums/enum_spa.xsd +108 -0
  184. mapproxy/test/schemas/inspire/common/1.0/enums/enum_swe.xsd +108 -0
  185. mapproxy/test/schemas/inspire/common/1.0/network.xsd +521 -0
  186. mapproxy/test/schemas/inspire/inspire_vs/1.0/inspire_vs.xsd +19 -0
  187. mapproxy/test/schemas/kml/2.2.0/ReadMe.txt +14 -0
  188. mapproxy/test/schemas/kml/2.2.0/atom-author-link.xsd +66 -0
  189. mapproxy/test/schemas/kml/2.2.0/ogckml22.xsd +1646 -0
  190. mapproxy/test/schemas/kml/2.2.0/xAL.xsd +1680 -0
  191. mapproxy/test/schemas/ows/1.1.0/ReadMe.txt +87 -0
  192. mapproxy/test/schemas/ows/1.1.0/ows19115subset.xsd +235 -0
  193. mapproxy/test/schemas/ows/1.1.0/owsAll.xsd +23 -0
  194. mapproxy/test/schemas/ows/1.1.0/owsCommon.xsd +157 -0
  195. mapproxy/test/schemas/ows/1.1.0/owsContents.xsd +86 -0
  196. mapproxy/test/schemas/ows/1.1.0/owsDataIdentification.xsd +127 -0
  197. mapproxy/test/schemas/ows/1.1.0/owsDomainType.xsd +279 -0
  198. mapproxy/test/schemas/ows/1.1.0/owsExceptionReport.xsd +76 -0
  199. mapproxy/test/schemas/ows/1.1.0/owsGetCapabilities.xsd +112 -0
  200. mapproxy/test/schemas/ows/1.1.0/owsGetResourceByID.xsd +51 -0
  201. mapproxy/test/schemas/ows/1.1.0/owsInputOutputData.xsd +59 -0
  202. mapproxy/test/schemas/ows/1.1.0/owsManifest.xsd +125 -0
  203. mapproxy/test/schemas/ows/1.1.0/owsOperationsMetadata.xsd +140 -0
  204. mapproxy/test/schemas/ows/1.1.0/owsServiceIdentification.xsd +60 -0
  205. mapproxy/test/schemas/ows/1.1.0/owsServiceProvider.xsd +47 -0
  206. mapproxy/test/schemas/sld/1.1.0/sld_capabilities.xsd +27 -0
  207. mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.dtd +353 -0
  208. mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.xml +188 -0
  209. mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.dtd +524 -0
  210. mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.xml +260 -0
  211. mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.dtd +273 -0
  212. mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.xml +303 -0
  213. mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.dtd +6 -0
  214. mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.xml +33 -0
  215. mapproxy/test/schemas/wms/1.1.1/OGC-exception.xsd +68 -0
  216. mapproxy/test/schemas/wms/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
  217. mapproxy/test/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd +274 -0
  218. mapproxy/test/schemas/wms/1.1.1/WMS_exception_1_1_1.dtd +5 -0
  219. mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.dtd +276 -0
  220. mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.xml +303 -0
  221. mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.dtd +6 -0
  222. mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.xml +33 -0
  223. mapproxy/test/schemas/wms/1.3.0/ReadMe.txt +8 -0
  224. mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xml +277 -0
  225. mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xsd +611 -0
  226. mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xml +34 -0
  227. mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xsd +28 -0
  228. mapproxy/test/schemas/wmsc/1.1.1/OGC-exception.xsd +68 -0
  229. mapproxy/test/schemas/wmsc/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
  230. mapproxy/test/schemas/wmsc/1.1.1/WMS_MS_Capabilities.dtd +283 -0
  231. mapproxy/test/schemas/wmsc/1.1.1/WMS_exception_1_1_1.dtd +5 -0
  232. mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.dtd +276 -0
  233. mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.xml +303 -0
  234. mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.dtd +6 -0
  235. mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.xml +33 -0
  236. mapproxy/test/schemas/wmts/1.0/ReadMe.txt +32 -0
  237. mapproxy/test/schemas/wmts/1.0/wmts.xsd +28 -0
  238. mapproxy/test/schemas/wmts/1.0/wmtsAbstract.wsdl +151 -0
  239. mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_request.xsd +38 -0
  240. mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_response.xsd +564 -0
  241. mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_request.xsd +57 -0
  242. mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_response.xsd +72 -0
  243. mapproxy/test/schemas/wmts/1.0/wmtsGetTile_request.xsd +91 -0
  244. mapproxy/test/schemas/wmts/1.0/wmtsKVP.xsd +76 -0
  245. mapproxy/test/schemas/wmts/1.0/wmtsPayload_response.xsd +70 -0
  246. mapproxy/test/schemas/xlink/1.0.0/ReadMe.txt +6 -0
  247. mapproxy/test/schemas/xlink/1.0.0/xlinks.xsd +122 -0
  248. mapproxy/test/schemas/xml.xsd +287 -0
  249. mapproxy/test/system/__init__.py +98 -0
  250. mapproxy/test/system/fixture/arcgis.yaml +57 -0
  251. mapproxy/test/system/fixture/auth.yaml +70 -0
  252. mapproxy/test/system/fixture/cache.mbtiles +0 -0
  253. mapproxy/test/system/fixture/cache_azureblob.yaml +59 -0
  254. mapproxy/test/system/fixture/cache_band_merge.yaml +73 -0
  255. mapproxy/test/system/fixture/cache_bulk_meta_tiles.yaml +24 -0
  256. mapproxy/test/system/fixture/cache_coverage.yaml +84 -0
  257. mapproxy/test/system/fixture/cache_data/dop_cache_EPSG3857/00/000/000/000/000/000/000.png +0 -0
  258. mapproxy/test/system/fixture/cache_data/wms_cache_EPSG900913/01/000/000/000/000/000/001.jpeg +0 -0
  259. mapproxy/test/system/fixture/cache_data/wms_cache_transparent_EPSG900913/01/000/000/000/000/000/001.png +0 -0
  260. mapproxy/test/system/fixture/cache_geopackage.yaml +56 -0
  261. mapproxy/test/system/fixture/cache_grid_names.yaml +50 -0
  262. mapproxy/test/system/fixture/cache_mbtiles.yaml +28 -0
  263. mapproxy/test/system/fixture/cache_s3.yaml +58 -0
  264. mapproxy/test/system/fixture/cache_source.yaml +81 -0
  265. mapproxy/test/system/fixture/combined_sources.yaml +130 -0
  266. mapproxy/test/system/fixture/coverage.yaml +77 -0
  267. mapproxy/test/system/fixture/demo.yaml +135 -0
  268. mapproxy/test/system/fixture/dimension.yaml +59 -0
  269. mapproxy/test/system/fixture/disable_storage.yaml +25 -0
  270. mapproxy/test/system/fixture/empty_ogrdata.geojson +1 -0
  271. mapproxy/test/system/fixture/formats.yaml +72 -0
  272. mapproxy/test/system/fixture/inspire.yaml +101 -0
  273. mapproxy/test/system/fixture/inspire_full.yaml +124 -0
  274. mapproxy/test/system/fixture/kml_layer.yaml +66 -0
  275. mapproxy/test/system/fixture/layer.yaml +260 -0
  276. mapproxy/test/system/fixture/layergroups.yaml +57 -0
  277. mapproxy/test/system/fixture/layergroups_root.yaml +106 -0
  278. mapproxy/test/system/fixture/legendgraphic.yaml +93 -0
  279. mapproxy/test/system/fixture/mapnik_source.yaml +66 -0
  280. mapproxy/test/system/fixture/mapproxy_export.yaml +12 -0
  281. mapproxy/test/system/fixture/mapserver.yaml +23 -0
  282. mapproxy/test/system/fixture/minimal_cgi.py +16 -0
  283. mapproxy/test/system/fixture/mixed_mode.yaml +49 -0
  284. mapproxy/test/system/fixture/multi_cache_layers.yaml +111 -0
  285. mapproxy/test/system/fixture/multiapp1.yaml +20 -0
  286. mapproxy/test/system/fixture/multiapp2.yaml +19 -0
  287. mapproxy/test/system/fixture/renderd_client.yaml +55 -0
  288. mapproxy/test/system/fixture/scalehints.yaml +70 -0
  289. mapproxy/test/system/fixture/seed.yaml +94 -0
  290. mapproxy/test/system/fixture/seed_mapproxy.yaml +39 -0
  291. mapproxy/test/system/fixture/seed_old.yaml +12 -0
  292. mapproxy/test/system/fixture/seed_timeouts.yaml +12 -0
  293. mapproxy/test/system/fixture/seed_timeouts_mapproxy.yaml +27 -0
  294. mapproxy/test/system/fixture/seedonly.yaml +51 -0
  295. mapproxy/test/system/fixture/sld.yaml +35 -0
  296. mapproxy/test/system/fixture/source_errors.yaml +84 -0
  297. mapproxy/test/system/fixture/source_errors_raise.yaml +82 -0
  298. mapproxy/test/system/fixture/tileservice_origin.yaml +26 -0
  299. mapproxy/test/system/fixture/tileservice_refresh.yaml +59 -0
  300. mapproxy/test/system/fixture/tilesource_minmax_res.yaml +22 -0
  301. mapproxy/test/system/fixture/util-conf-base-grids.yaml +5 -0
  302. mapproxy/test/system/fixture/util-conf-overwrite.yaml +13 -0
  303. mapproxy/test/system/fixture/util-conf-wms-111-cap.xml +90 -0
  304. mapproxy/test/system/fixture/util_grids.yaml +29 -0
  305. mapproxy/test/system/fixture/util_wms_capabilities111.xml +130 -0
  306. mapproxy/test/system/fixture/util_wms_capabilities130.xml +100 -0
  307. mapproxy/test/system/fixture/util_wms_capabilities_service_exception.xml +5 -0
  308. mapproxy/test/system/fixture/watermark.yaml +50 -0
  309. mapproxy/test/system/fixture/wms_srs_extent.yaml +39 -0
  310. mapproxy/test/system/fixture/wms_versions.yaml +38 -0
  311. mapproxy/test/system/fixture/wmts.yaml +134 -0
  312. mapproxy/test/system/fixture/wmts_dimensions.yaml +57 -0
  313. mapproxy/test/system/fixture/xslt_featureinfo.yaml +54 -0
  314. mapproxy/test/system/fixture/xslt_featureinfo_input.yaml +51 -0
  315. mapproxy/test/system/test_arcgis.py +156 -0
  316. mapproxy/test/system/test_auth.py +1133 -0
  317. mapproxy/test/system/test_behind_proxy.py +75 -0
  318. mapproxy/test/system/test_bulk_meta_tiles.py +106 -0
  319. mapproxy/test/system/test_cache_azureblob.py +127 -0
  320. mapproxy/test/system/test_cache_band_merge.py +103 -0
  321. mapproxy/test/system/test_cache_coverage.py +168 -0
  322. mapproxy/test/system/test_cache_geopackage.py +144 -0
  323. mapproxy/test/system/test_cache_grid_names.py +89 -0
  324. mapproxy/test/system/test_cache_mbtiles.py +85 -0
  325. mapproxy/test/system/test_cache_s3.py +115 -0
  326. mapproxy/test/system/test_cache_source.py +146 -0
  327. mapproxy/test/system/test_combined_sources.py +335 -0
  328. mapproxy/test/system/test_coverage.py +140 -0
  329. mapproxy/test/system/test_decorate_img.py +214 -0
  330. mapproxy/test/system/test_demo.py +56 -0
  331. mapproxy/test/system/test_demo_with_extra_service.py +57 -0
  332. mapproxy/test/system/test_dimensions.py +279 -0
  333. mapproxy/test/system/test_disable_storage.py +42 -0
  334. mapproxy/test/system/test_formats.py +219 -0
  335. mapproxy/test/system/test_inspire_vs.py +173 -0
  336. mapproxy/test/system/test_kml.py +264 -0
  337. mapproxy/test/system/test_layergroups.py +160 -0
  338. mapproxy/test/system/test_legendgraphic.py +308 -0
  339. mapproxy/test/system/test_mapnik.py +162 -0
  340. mapproxy/test/system/test_mapserver.py +83 -0
  341. mapproxy/test/system/test_mixed_mode_format.py +201 -0
  342. mapproxy/test/system/test_multi_cache_layers.py +169 -0
  343. mapproxy/test/system/test_multiapp.py +92 -0
  344. mapproxy/test/system/test_refresh.py +206 -0
  345. mapproxy/test/system/test_renderd_client.py +304 -0
  346. mapproxy/test/system/test_response_headers.py +54 -0
  347. mapproxy/test/system/test_scalehints.py +140 -0
  348. mapproxy/test/system/test_seed.py +425 -0
  349. mapproxy/test/system/test_seed_only.py +93 -0
  350. mapproxy/test/system/test_sld.py +120 -0
  351. mapproxy/test/system/test_source_errors.py +377 -0
  352. mapproxy/test/system/test_tilesource_minmax_res.py +54 -0
  353. mapproxy/test/system/test_tms.py +277 -0
  354. mapproxy/test/system/test_tms_origin.py +46 -0
  355. mapproxy/test/system/test_util_conf.py +434 -0
  356. mapproxy/test/system/test_util_export.py +210 -0
  357. mapproxy/test/system/test_util_grids.py +88 -0
  358. mapproxy/test/system/test_util_wms_capabilities.py +182 -0
  359. mapproxy/test/system/test_watermark.py +91 -0
  360. mapproxy/test/system/test_wms.py +1616 -0
  361. mapproxy/test/system/test_wms_srs_extent.py +165 -0
  362. mapproxy/test/system/test_wms_version.py +85 -0
  363. mapproxy/test/system/test_wmsc.py +116 -0
  364. mapproxy/test/system/test_wmts.py +334 -0
  365. mapproxy/test/system/test_wmts_dimensions.py +206 -0
  366. mapproxy/test/system/test_wmts_restful.py +198 -0
  367. mapproxy/test/system/test_xslt_featureinfo.py +423 -0
  368. mapproxy/test/test_http_helper.py +217 -0
  369. mapproxy/test/unit/__init__.py +0 -0
  370. mapproxy/test/unit/epsg +2 -0
  371. mapproxy/test/unit/polygons/polygons.dbf +0 -0
  372. mapproxy/test/unit/polygons/polygons.shp +0 -0
  373. mapproxy/test/unit/polygons/polygons.shx +0 -0
  374. mapproxy/test/unit/test_async.py +242 -0
  375. mapproxy/test/unit/test_auth.py +430 -0
  376. mapproxy/test/unit/test_cache.py +1356 -0
  377. mapproxy/test/unit/test_cache_azureblob.py +97 -0
  378. mapproxy/test/unit/test_cache_compact.py +324 -0
  379. mapproxy/test/unit/test_cache_couchdb.py +118 -0
  380. mapproxy/test/unit/test_cache_geopackage.py +256 -0
  381. mapproxy/test/unit/test_cache_redis.py +123 -0
  382. mapproxy/test/unit/test_cache_riak.py +80 -0
  383. mapproxy/test/unit/test_cache_s3.py +93 -0
  384. mapproxy/test/unit/test_cache_tile.py +477 -0
  385. mapproxy/test/unit/test_client.py +488 -0
  386. mapproxy/test/unit/test_client_arcgis.py +74 -0
  387. mapproxy/test/unit/test_client_cgi.py +140 -0
  388. mapproxy/test/unit/test_collections.py +116 -0
  389. mapproxy/test/unit/test_concat_legends.py +37 -0
  390. mapproxy/test/unit/test_conf_loader.py +1267 -0
  391. mapproxy/test/unit/test_conf_validator.py +427 -0
  392. mapproxy/test/unit/test_config.py +118 -0
  393. mapproxy/test/unit/test_decorate_img.py +185 -0
  394. mapproxy/test/unit/test_exceptions.py +270 -0
  395. mapproxy/test/unit/test_featureinfo.py +313 -0
  396. mapproxy/test/unit/test_file_lock_load.py +49 -0
  397. mapproxy/test/unit/test_geom.py +512 -0
  398. mapproxy/test/unit/test_grid.py +1279 -0
  399. mapproxy/test/unit/test_image.py +1051 -0
  400. mapproxy/test/unit/test_image_mask.py +181 -0
  401. mapproxy/test/unit/test_image_messages.py +209 -0
  402. mapproxy/test/unit/test_image_options.py +160 -0
  403. mapproxy/test/unit/test_isodate.py +118 -0
  404. mapproxy/test/unit/test_multiapp.py +163 -0
  405. mapproxy/test/unit/test_ogr_reader.py +51 -0
  406. mapproxy/test/unit/test_request.py +745 -0
  407. mapproxy/test/unit/test_request_wmts.py +178 -0
  408. mapproxy/test/unit/test_response.py +78 -0
  409. mapproxy/test/unit/test_seed.py +365 -0
  410. mapproxy/test/unit/test_seed_cachelock.py +91 -0
  411. mapproxy/test/unit/test_srs.py +215 -0
  412. mapproxy/test/unit/test_tiled_source.py +122 -0
  413. mapproxy/test/unit/test_tilefilter.py +31 -0
  414. mapproxy/test/unit/test_times.py +25 -0
  415. mapproxy/test/unit/test_timeutils.py +50 -0
  416. mapproxy/test/unit/test_util_conf_utils.py +75 -0
  417. mapproxy/test/unit/test_utils.py +476 -0
  418. mapproxy/test/unit/test_wms_capabilities.py +44 -0
  419. mapproxy/test/unit/test_wms_layer.py +113 -0
  420. mapproxy/test/unit/test_yaml.py +68 -0
  421. mapproxy/tilefilter.py +61 -0
  422. mapproxy/util/__init__.py +0 -0
  423. mapproxy/util/async_.py +229 -0
  424. mapproxy/util/collections.py +134 -0
  425. mapproxy/util/coverage.py +337 -0
  426. mapproxy/util/ext/__init__.py +14 -0
  427. mapproxy/util/ext/dictspec/__init__.py +1 -0
  428. mapproxy/util/ext/dictspec/spec.py +131 -0
  429. mapproxy/util/ext/dictspec/test/__init__.py +0 -0
  430. mapproxy/util/ext/dictspec/test/test_validator.py +278 -0
  431. mapproxy/util/ext/dictspec/validator.py +194 -0
  432. mapproxy/util/ext/local.py +198 -0
  433. mapproxy/util/ext/lockfile.py +140 -0
  434. mapproxy/util/ext/odict.py +321 -0
  435. mapproxy/util/ext/serving.py +491 -0
  436. mapproxy/util/ext/tempita/__init__.py +1093 -0
  437. mapproxy/util/ext/tempita/_looper.py +163 -0
  438. mapproxy/util/ext/tempita/string_utils.py +24 -0
  439. mapproxy/util/ext/wmsparse/__init__.py +3 -0
  440. mapproxy/util/ext/wmsparse/duration.py +600 -0
  441. mapproxy/util/ext/wmsparse/parse.py +307 -0
  442. mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
  443. mapproxy/util/ext/wmsparse/test/test_parse.py +111 -0
  444. mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
  445. mapproxy/util/ext/wmsparse/test/wms-example-111.xml +90 -0
  446. mapproxy/util/ext/wmsparse/test/wms-example-130.xml +120 -0
  447. mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
  448. mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
  449. mapproxy/util/ext/wmsparse/util.py +189 -0
  450. mapproxy/util/fs.py +164 -0
  451. mapproxy/util/geom.py +307 -0
  452. mapproxy/util/lib.py +117 -0
  453. mapproxy/util/lock.py +171 -0
  454. mapproxy/util/ogr.py +247 -0
  455. mapproxy/util/py.py +75 -0
  456. mapproxy/util/times.py +78 -0
  457. mapproxy/util/yaml.py +58 -0
  458. mapproxy/version.py +33 -0
  459. mapproxy/wsgiapp.py +167 -0
mapproxy/test/http.py ADDED
@@ -0,0 +1,511 @@
1
+ # This file is part of the MapProxy project.
2
+ # Copyright (C) 2010 Omniscale <http://omniscale.de>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from __future__ import print_function
17
+
18
+
19
+ import re
20
+ import threading
21
+ import sys
22
+ import socket
23
+ import errno
24
+ import time
25
+ import base64
26
+ from contextlib import contextmanager
27
+ from urllib.parse import parse_qsl, urlsplit
28
+
29
+ from mapproxy.util.py import reraise
30
+ from http.server import HTTPServer as HTTPServer_, BaseHTTPRequestHandler
31
+
32
+
33
+ class RequestsMismatchError(AssertionError):
34
+ def __init__(self, assertions):
35
+ self.assertions = assertions
36
+
37
+ def __str__(self):
38
+ assertions = []
39
+ for assertion in self.assertions:
40
+ assertions.append(text_indent(str(assertion), ' ', ' - '))
41
+ return 'requests mismatch:\n' + '\n'.join(assertions)
42
+
43
+
44
+ class RequestError(str):
45
+ pass
46
+
47
+
48
+ def text_indent(text, indent, first_indent=None):
49
+ if first_indent is None:
50
+ first_indent = indent
51
+
52
+ text = first_indent + text
53
+ return text.replace('\n', '\n' + indent)
54
+
55
+
56
+ class RequestMismatch(object):
57
+ def __init__(self, msg, expected, actual):
58
+ self.msg = msg
59
+ self.expected = expected
60
+ self.actual = actual
61
+
62
+ def __str__(self):
63
+ return ('requests mismatch (%s), expected:\n' % self.msg +
64
+ text_indent(str(self.expected), ' ') +
65
+ '\n got:\n' + text_indent(str(self.actual), ' '))
66
+
67
+
68
+ class HTTPServer(HTTPServer_):
69
+ allow_reuse_address = True
70
+
71
+ def handle_error(self, request, client_address):
72
+ _exc_class, exc, _tb = sys.exc_info()
73
+ if isinstance(exc, socket.error):
74
+ if exc.errno == errno.EPIPE:
75
+ # suppres 'Broken pipe' errors raised in timeout tests
76
+ return
77
+ HTTPServer_.handle_error(self, request, client_address)
78
+
79
+
80
+ class ThreadedStopableHTTPServer(threading.Thread):
81
+ def __init__(self, address, requests_responses, unordered=False, query_comparator=None):
82
+ threading.Thread.__init__(self, **{'group': None})
83
+ self.requests_responses = requests_responses
84
+ self.daemon = True
85
+ self.sucess = False
86
+ self.shutdown = False
87
+ self.httpd = HTTPServer(address, mock_http_handler(requests_responses,
88
+ unordered=unordered, query_comparator=query_comparator))
89
+ self.httpd.timeout = 1.0
90
+ self.assertions = self.httpd.assertions = []
91
+
92
+ @property
93
+ def http_port(self):
94
+ return self.httpd.socket.getsockname()[1]
95
+
96
+ def run(self):
97
+ while self.requests_responses:
98
+ if self.shutdown:
99
+ break
100
+ self.httpd.handle_request()
101
+ if self.requests_responses:
102
+ missing_req = [req for req, resp in self.requests_responses]
103
+ self.assertions.append(
104
+ RequestError('missing requests: ' + ','.join(map(str, missing_req)))
105
+ )
106
+ if not self.assertions:
107
+ self.sucess = True
108
+ # force socket close so next test can bind to same address
109
+ self.httpd.socket.close()
110
+
111
+
112
+ class ThreadedSingleRequestHTTPServer(threading.Thread):
113
+ def __init__(self, address, request_handler):
114
+ threading.Thread.__init__(self, **{'group': None})
115
+ self.daemon = True
116
+ self.sucess = False
117
+ self.shutdown = False
118
+ self.httpd = HTTPServer(address, request_handler)
119
+ self.httpd.timeout = 1.0
120
+ self.assertions = self.httpd.assertions = []
121
+
122
+ def run(self):
123
+ self.httpd.handle_request()
124
+ if not self.assertions:
125
+ self.sucess = True
126
+ # force socket close so next test can bind to same address
127
+ self.httpd.socket.close()
128
+
129
+
130
+ def mock_http_handler(requests_responses, unordered=False, query_comparator=None):
131
+ if query_comparator is None:
132
+ query_comparator = query_eq
133
+
134
+ class MockHTTPHandler(BaseHTTPRequestHandler):
135
+ def do_HEAD(self):
136
+ self.query_data = self.path
137
+ return self.do_mock_request('HEAD')
138
+
139
+ def do_GET(self):
140
+ self.query_data = self.path
141
+ return self.do_mock_request('GET')
142
+
143
+ def do_POST(self):
144
+ length = int(self.headers['content-length'])
145
+ self.query_data = self.path + '?' + self.rfile.read(length).decode('utf-8')
146
+ return self.do_mock_request('POST')
147
+
148
+ def _matching_req_resp(self):
149
+ if len(requests_responses) == 0:
150
+ return None, None
151
+ if unordered:
152
+ for req_resp in requests_responses:
153
+ req, resp = req_resp
154
+ if query_comparator(req['path'], self.query_data):
155
+ requests_responses.remove(req_resp)
156
+ return req, resp
157
+ return None, None
158
+ else:
159
+ return requests_responses.pop(0)
160
+
161
+ def do_mock_request(self, method):
162
+ req, resp = self._matching_req_resp()
163
+ if not req:
164
+ self.server.assertions.append(
165
+ RequestError('got unexpected request: %s' % self.query_data)
166
+ )
167
+ return
168
+ if 'method' in req:
169
+ if req['method'] != method:
170
+ self.server.assertions.append(
171
+ RequestMismatch('unexpected method', req['method'], method)
172
+ )
173
+ self.server.shutdown = True
174
+ if req.get('require_basic_auth', False):
175
+ if 'Authorization' not in self.headers:
176
+ requests_responses.insert(0, (req, resp)) # push back
177
+ self.send_response(401)
178
+ self.send_header('WWW-Authenticate', 'Basic realm="Secure Area"')
179
+ self.end_headers()
180
+ self.wfile.write(b'no access')
181
+ return
182
+ if req.get('headers'):
183
+ for k, v in req['headers'].items():
184
+ if k not in self.headers:
185
+ self.server.assertions.append(
186
+ RequestMismatch('missing header', k, self.headers)
187
+ )
188
+ elif self.headers[k] != v:
189
+ self.server.assertions.append(
190
+ RequestMismatch('header mismatch', '%s: %s' % (k, v), self.headers)
191
+ )
192
+ if not query_comparator(req['path'], self.query_data):
193
+ self.server.assertions.append(
194
+ RequestMismatch('requests differ', req['path'], self.query_data)
195
+ )
196
+ query_actual = set(query_to_dict(self.query_data).items())
197
+ query_expected = set(query_to_dict(req['path']).items())
198
+ self.server.assertions.append(
199
+ RequestMismatch('requests params differ', query_expected -
200
+ query_actual, query_actual - query_expected)
201
+ )
202
+ self.server.shutdown = True
203
+ if 'req_assert_function' in req:
204
+ if not req['req_assert_function'](self):
205
+ self.server.assertions.append(
206
+ RequestError('req_assert_function failed')
207
+ )
208
+ self.server.shutdown = True
209
+ if 'duration' in resp:
210
+ time.sleep(float(resp['duration']))
211
+ self.start_response(resp)
212
+ if resp.get('body_file'):
213
+ with open(resp['body_file'], 'rb') as f:
214
+ self.wfile.write(f.read())
215
+ else:
216
+ if 'body' in resp:
217
+ self.wfile.write(resp['body'])
218
+ else:
219
+ self.wfile.write(b'')
220
+ if not requests_responses:
221
+ self.server.shutdown = True
222
+ return
223
+
224
+ def start_response(self, resp):
225
+ self.send_response(int(resp.get('status', '200')))
226
+ if 'headers' in resp:
227
+ for key, value in resp['headers'].items():
228
+ self.send_header(key, value)
229
+ self.end_headers()
230
+
231
+ def log_request(self, code, size=None):
232
+ pass
233
+
234
+ return MockHTTPHandler
235
+
236
+
237
+ class MockServ(object):
238
+ def __init__(self, port=0, host='localhost', unordered=False, bbox_aware_query_comparator=False):
239
+ self._requested_port = port
240
+ self.port = port
241
+ self.host = host
242
+ self.requests_responses = []
243
+ self.unordered = unordered
244
+ self.query_comparator = None
245
+ if bbox_aware_query_comparator:
246
+ self.query_comparator = wms_query_eq
247
+ self._init_thread()
248
+
249
+ def _init_thread(self):
250
+ self._thread = ThreadedStopableHTTPServer((self.host, self._requested_port),
251
+ [], unordered=self.unordered, query_comparator=self.query_comparator)
252
+ if self._requested_port == 0:
253
+ self.port = self._thread.http_port
254
+ self.address = (self.host, self.port)
255
+
256
+ def reset(self):
257
+ self._init_thread()
258
+
259
+ @property
260
+ def base_url(self):
261
+ return 'http://localhost:%d' % (self.port, )
262
+
263
+ def expects(self, path, method='GET', headers=None):
264
+ headers = headers or ()
265
+ self.requests_responses.append(
266
+ (dict(path=path, method=method, headers=headers), {'body': b''}))
267
+ return self
268
+
269
+ def returns(self, body=None, body_file=None, status_code=200, headers=None):
270
+ assert body or body_file
271
+ headers = headers or {}
272
+ self.requests_responses[-1][1].update(
273
+ body=body, body_file=body_file, status=status_code, headers=headers)
274
+ return self
275
+
276
+ def __enter__(self):
277
+ # copy request_responses to be able to reuse it after .reset()
278
+ self._thread.requests_responses[:] = self.requests_responses
279
+ self._thread.start()
280
+
281
+ def __exit__(self, type, value, traceback):
282
+ self._thread.shutdown = True
283
+ self._thread.join(30)
284
+
285
+ if not self._thread.sucess and value:
286
+ print('requests to mock httpd did not '
287
+ 'match expectations:\n %s' % RequestsMismatchError(self._thread.assertions))
288
+ if value:
289
+ raise reraise((type, value, traceback))
290
+ if not self._thread.sucess:
291
+ raise RequestsMismatchError(self._thread.assertions)
292
+
293
+
294
+ def wms_query_eq(expected, actual):
295
+ """
296
+ >>> wms_query_eq('bAR=baz&foo=bizz&bbOX=0,0,100000,100000', 'foO=bizz&BBOx=-.0001,0.01,99999.99,100000.09&bar=baz')
297
+ True
298
+ >>> wms_query_eq('bAR=baz&foo=bizz&bbOX=0,0,100000,100000', 'foO=bizz&BBOx=-.0001,0.01,99999.99,100000.11&bar=baz')
299
+ False
300
+ >>> wms_query_eq('/service?bar=baz&fOO=bizz', 'foo=bizz&bar=baz')
301
+ False
302
+ >>> wms_query_eq('/1/2/3.png', '/1/2/3.png')
303
+ True
304
+ >>> wms_query_eq('/1/2/3.png', '/1/2/0.png')
305
+ False
306
+ """
307
+ from mapproxy.srs import bbox_equals
308
+ if path_from_query(expected) != path_from_query(actual):
309
+ return False
310
+
311
+ expected = query_to_dict(expected)
312
+ actual = query_to_dict(actual)
313
+
314
+ if 'bbox' in expected and 'bbox' in actual:
315
+ expected = expected.copy()
316
+ expected_bbox = [float(x) for x in expected.pop('bbox').split(',')]
317
+ actual = actual.copy()
318
+ actual_bbox = [float(x) for x in actual.pop('bbox').split(',')]
319
+ if expected != actual:
320
+ return False
321
+ if not bbox_equals(expected_bbox, actual_bbox):
322
+ return False
323
+ else:
324
+ if expected != actual:
325
+ return False
326
+
327
+ return True
328
+
329
+
330
+ numbers_only = re.compile(r'^-?\d+\.\d+(,-?\d+\.\d+)*$')
331
+
332
+
333
+ def query_eq(expected, actual):
334
+ """
335
+ >>> query_eq('bAR=baz&foo=bizz', 'foO=bizz&bar=baz')
336
+ True
337
+ >>> query_eq('/service?bar=baz&fOO=bizz', 'foo=bizz&bar=baz')
338
+ False
339
+ >>> query_eq('/1/2/3.png', '/1/2/3.png')
340
+ True
341
+ >>> query_eq('/1/2/3.png', '/1/2/0.png')
342
+ False
343
+ >>> query_eq('/map?point=2.9999999999,1.00000000001', '/map?point=3.0,1.0')
344
+ True
345
+ """
346
+
347
+ if path_from_query(expected) != path_from_query(actual):
348
+ return False
349
+
350
+ expected = query_to_dict(expected)
351
+ actual = query_to_dict(actual)
352
+
353
+ if set(expected.keys()) != set(actual.keys()):
354
+ return False
355
+
356
+ for ke, ve in expected.items():
357
+ if numbers_only.match(ve):
358
+ if not float_string_almost_eq(ve, actual[ke]):
359
+ return False
360
+ else:
361
+ if ve != actual[ke]:
362
+ return False
363
+
364
+ return True
365
+
366
+
367
+ def float_string_almost_eq(expected, actual):
368
+ """
369
+ Compares if two strings with comma-separated floats are almost equal.
370
+ Strings must contain floats.
371
+
372
+ >>> float_string_almost_eq('12345678900', '12345678901')
373
+ False
374
+ >>> float_string_almost_eq('12345678900.0', '12345678901.0')
375
+ True
376
+
377
+ >>> float_string_almost_eq('12345678900.0,-3.0', '12345678901.0,-2.9999999999')
378
+ True
379
+ """
380
+ if not numbers_only.match(expected) or not numbers_only.match(actual):
381
+ return False
382
+
383
+ expected_nums = [float(x) for x in expected.split(',')]
384
+ actual_nums = [float(x) for x in actual.split(',')]
385
+
386
+ if len(expected_nums) != len(actual_nums):
387
+ return False
388
+
389
+ for e, a in zip(expected_nums, actual_nums):
390
+ if abs(e - a) > abs((e+a)/2)/10e9:
391
+ return False
392
+
393
+ return True
394
+
395
+
396
+ def assert_query_eq(expected, actual, fuzzy_number_compare=False):
397
+ path_actual = path_from_query(actual)
398
+ path_expected = path_from_query(expected)
399
+ assert path_expected == path_actual, path_expected + '!=' + path_actual
400
+
401
+ query_actual = set(query_to_dict(actual).items())
402
+ query_expected = set(query_to_dict(expected).items())
403
+
404
+ if fuzzy_number_compare:
405
+ equal = query_eq(expected, actual)
406
+ else:
407
+ equal = query_expected == query_actual
408
+ assert equal, '%s != %s\t%s|%s' % (
409
+ expected, actual, query_expected - query_actual, query_actual - query_expected)
410
+
411
+
412
+ def path_from_query(query):
413
+ """
414
+ >>> path_from_query('/service?foo=bar')
415
+ '/service'
416
+ >>> path_from_query('/1/2/3.png')
417
+ '/1/2/3.png'
418
+ >>> path_from_query('foo=bar')
419
+ ''
420
+ """
421
+ if not ('&' in query or '=' in query):
422
+ return query
423
+ if '?' in query:
424
+ return query.split('?', 1)[0]
425
+ return ''
426
+
427
+
428
+ def query_to_dict(query):
429
+ """
430
+ >>> sorted(query_to_dict('/service?bar=baz&foo=bizz').items())
431
+ [('bar', 'baz'), ('foo', 'bizz')]
432
+ >>> sorted(query_to_dict('bar=baz&foo=bizz').items())
433
+ [('bar', 'baz'), ('foo', 'bizz')]
434
+ """
435
+ if not ('&' in query or '=' in query):
436
+ return {}
437
+ d = {}
438
+ if '?' in query:
439
+ query = query.split('?', 1)[-1]
440
+ for key, value in parse_qsl(query):
441
+ d[key.lower()] = value
442
+ return d
443
+
444
+
445
+ def assert_url_eq(url1, url2):
446
+ parts1 = urlsplit(url1)
447
+ parts2 = urlsplit(url2)
448
+
449
+ assert parts1[0] == parts2[0], '%s != %s (%s)' % (url1, url2, 'schema')
450
+ assert parts1[1] == parts2[1], '%s != %s (%s)' % (url1, url2, 'location')
451
+ assert parts1[2] == parts2[2], '%s != %s (%s)' % (url1, url2, 'path')
452
+ assert query_eq(parts1[3], parts2[3]), '%s != %s (%s)' % (url1, url2, 'query')
453
+ assert parts1[4] == parts2[4], '%s != %s (%s)' % (url1, url2, 'fragment')
454
+
455
+
456
+ @contextmanager
457
+ def mock_httpd(address, requests_responses, unordered=False, bbox_aware_query_comparator=False):
458
+ if bbox_aware_query_comparator:
459
+ query_comparator = wms_query_eq
460
+ else:
461
+ query_comparator = query_eq
462
+ t = ThreadedStopableHTTPServer(address, requests_responses, unordered=unordered,
463
+ query_comparator=query_comparator)
464
+ t.start()
465
+ try:
466
+ yield
467
+ except Exception:
468
+ if not t.sucess:
469
+ print(str(RequestsMismatchError(t.assertions)))
470
+ raise
471
+ finally:
472
+ t.shutdown = True
473
+ t.join(30)
474
+ if not t.sucess:
475
+ raise RequestsMismatchError(t.assertions)
476
+
477
+
478
+ @contextmanager
479
+ def mock_single_req_httpd(address, request_handler):
480
+ t = ThreadedSingleRequestHTTPServer(address, request_handler)
481
+ t.start()
482
+ try:
483
+ yield
484
+ except Exception:
485
+ if not t.sucess:
486
+ print(str(RequestsMismatchError(t.assertions)))
487
+ raise
488
+ finally:
489
+ t.shutdown = True
490
+ t.join(30)
491
+ if not t.sucess:
492
+ raise RequestsMismatchError(t.assertions)
493
+
494
+
495
+ def make_wsgi_env(query_string, extra_environ={}):
496
+ env = {'QUERY_STRING': query_string,
497
+ 'wsgi.url_scheme': 'http',
498
+ 'HTTP_HOST': 'localhost',
499
+ }
500
+ env.update(extra_environ)
501
+ return env
502
+
503
+
504
+ def basic_auth_value(username, password):
505
+ return base64.b64encode(('%s:%s' % (username, password)).encode('utf-8'))
506
+
507
+
508
+ def assert_no_cache(resp):
509
+ assert resp.headers["Pragma"] == "no-cache"
510
+ assert resp.headers["Expires"] == "-1"
511
+ assert resp.cache_control.no_store is True