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
@@ -0,0 +1,600 @@
1
+ # This file is part of the MapProxy project.
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ # Disclaimer:
15
+ # This code is based on isodate from: https://github.com/gweis/isodate by 2009, Gerhard Weis
16
+ # Some functions were extracted and modified from original project.
17
+
18
+ from decimal import Decimal, ROUND_FLOOR
19
+ from datetime import timedelta, tzinfo as tz, date, time, datetime
20
+ import re
21
+
22
+ TIME_REGEX_CACHE = []
23
+ # used to cache regular expressions to parse ISO time strings.
24
+ DATE_REGEX_CACHE = {}
25
+ # A dictionary to cache pre-compiled regular expressions.
26
+ # A set of regular expressions is identified, by number of year digits allowed
27
+ # and whether a plus/minus sign is required or not. (This option is changeable
28
+ # only for 4 digit years).
29
+ TZ_REGEX = r"(?P<tzname>(Z|(?P<tzsign>[+-])"\
30
+ r"(?P<tzhour>[0-9]{2})(:?(?P<tzmin>[0-9]{2}))?)?)"
31
+
32
+ TZ_EXT = '%Z'
33
+ ZERO = timedelta(0)
34
+
35
+
36
+ def build_date_regexps(yeardigits=4, expanded=False):
37
+ '''
38
+ Compile set of regular expressions to parse ISO dates. The expressions will
39
+ be created only if they are not already in REGEX_CACHE.
40
+ It is necessary to fix the number of year digits, else it is not possible
41
+ to automatically distinguish between various ISO date formats.
42
+ ISO 8601 allows more than 4 digit years, on prior agreement, but then a +/-
43
+ sign is required (expanded format). To support +/- sign for 4 digit years,
44
+ the expanded parameter needs to be set to True.
45
+ '''
46
+ if yeardigits != 4:
47
+ expanded = True
48
+ if (yeardigits, expanded) not in DATE_REGEX_CACHE:
49
+ cache_entry = []
50
+ # ISO 8601 expanded DATE formats allow an arbitrary number of year
51
+ # digits with a leading +/- sign.
52
+ if expanded:
53
+ sign = 1
54
+ else:
55
+ sign = 0
56
+ # 1. complete dates:
57
+ # YYYY-MM-DD or +- YYYYYY-MM-DD... extended date format
58
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
59
+ r"-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})"
60
+ % (sign, yeardigits)))
61
+ # YYYYMMDD or +- YYYYYYMMDD... basic date format
62
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
63
+ r"(?P<month>[0-9]{2})(?P<day>[0-9]{2})"
64
+ % (sign, yeardigits)))
65
+ # 2. complete week dates:
66
+ # YYYY-Www-D or +-YYYYYY-Www-D ... extended week date
67
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
68
+ r"-W(?P<week>[0-9]{2})-(?P<day>[0-9]{1})"
69
+ % (sign, yeardigits)))
70
+ # YYYYWwwD or +-YYYYYYWwwD ... basic week date
71
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})W"
72
+ r"(?P<week>[0-9]{2})(?P<day>[0-9]{1})"
73
+ % (sign, yeardigits)))
74
+ # 3. ordinal dates:
75
+ # YYYY-DDD or +-YYYYYY-DDD ... extended format
76
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
77
+ r"-(?P<day>[0-9]{3})"
78
+ % (sign, yeardigits)))
79
+ # YYYYDDD or +-YYYYYYDDD ... basic format
80
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
81
+ r"(?P<day>[0-9]{3})"
82
+ % (sign, yeardigits)))
83
+ # 4. week dates:
84
+ # YYYY-Www or +-YYYYYY-Www ... extended reduced accuracy week date
85
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
86
+ r"-W(?P<week>[0-9]{2})"
87
+ % (sign, yeardigits)))
88
+ # YYYYWww or +-YYYYYYWww ... basic reduced accuracy week date
89
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})W"
90
+ r"(?P<week>[0-9]{2})"
91
+ % (sign, yeardigits)))
92
+ # 5. month dates:
93
+ # YYY-MM or +-YYYYYY-MM ... reduced accuracy specific month
94
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
95
+ r"-(?P<month>[0-9]{2})"
96
+ % (sign, yeardigits)))
97
+ # YYYMM or +-YYYYYYMM ... basic incomplete month date format
98
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
99
+ r"(?P<month>[0-9]{2})"
100
+ % (sign, yeardigits)))
101
+ # 6. year dates:
102
+ # YYYY or +-YYYYYY ... reduced accuracy specific year
103
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
104
+ % (sign, yeardigits)))
105
+ # 7. century dates:
106
+ # YY or +-YYYY ... reduced accuracy specific century
107
+ cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}"
108
+ r"(?P<century>[0-9]{%d})"
109
+ % (sign, yeardigits - 2)))
110
+
111
+ DATE_REGEX_CACHE[(yeardigits, expanded)] = cache_entry
112
+ return DATE_REGEX_CACHE[(yeardigits, expanded)]
113
+
114
+
115
+ def build_time_regexps():
116
+ '''
117
+ Build regular expressions to parse ISO time string.
118
+ The regular expressions are compiled and stored in TIME_REGEX_CACHE
119
+ for later reuse.
120
+ '''
121
+ if not TIME_REGEX_CACHE:
122
+ # ISO 8601 time representations allow decimal fractions on least
123
+ # significant time component. Command and Full Stop are both valid
124
+ # fraction separators.
125
+ # The letter 'T' is allowed as time designator in front of a time
126
+ # expression.
127
+ # Immediately after a time expression, a time zone definition is
128
+ # allowed.
129
+ # a TZ may be missing (local time), be a 'Z' for UTC or a string of
130
+ # +-hh:mm where the ':mm' part can be skipped.
131
+ # TZ information patterns:
132
+ # ''
133
+ # Z
134
+ # +-hh:mm
135
+ # +-hhmm
136
+ # +-hh =>
137
+ # isotzinfo.TZ_REGEX
138
+ # 1. complete time:
139
+ # hh:mm:ss.ss ... extended format
140
+ TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2}):"
141
+ r"(?P<minute>[0-9]{2}):"
142
+ r"(?P<second>[0-9]{2}"
143
+ r"([,.][0-9]+)?)" + TZ_REGEX))
144
+ # hhmmss.ss ... basic format
145
+ TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2})"
146
+ r"(?P<minute>[0-9]{2})"
147
+ r"(?P<second>[0-9]{2}"
148
+ r"([,.][0-9]+)?)" + TZ_REGEX))
149
+ # 2. reduced accuracy:
150
+ # hh:mm.mm ... extended format
151
+ TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2}):"
152
+ r"(?P<minute>[0-9]{2}"
153
+ r"([,.][0-9]+)?)" + TZ_REGEX))
154
+ # hhmm.mm ... basic format
155
+ TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2})"
156
+ r"(?P<minute>[0-9]{2}"
157
+ r"([,.][0-9]+)?)" + TZ_REGEX))
158
+ # hh.hh ... basic format
159
+ TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2}"
160
+ r"([,.][0-9]+)?)" + TZ_REGEX))
161
+ return TIME_REGEX_CACHE
162
+
163
+
164
+ class FixedOffset(tz):
165
+ '''
166
+ A class building tzinfo objects for fixed-offset time zones.
167
+ Note that FixedOffset(0, 0, "UTC") or FixedOffset() is a different way to
168
+ build a UTC tzinfo object.
169
+ '''
170
+
171
+ def __init__(self, offset_hours=0, offset_minutes=0, name="UTC"):
172
+ '''
173
+ Initialise an instance with time offset and name.
174
+ The time offset should be positive for time zones east of UTC
175
+ and negate for time zones west of UTC.
176
+ '''
177
+ self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes)
178
+ self.__name = name
179
+
180
+ def utcoffset(self, dt):
181
+ '''
182
+ Return offset from UTC in minutes of UTC.
183
+ '''
184
+ return self.__offset
185
+
186
+ def tzname(self, dt):
187
+ '''
188
+ Return the time zone name corresponding to the datetime object dt, as a
189
+ string.
190
+ '''
191
+ return self.__name
192
+
193
+ def dst(self, dt):
194
+ '''
195
+ Return the daylight saving time (DST) adjustment, in minutes east of
196
+ UTC.
197
+ '''
198
+ return ZERO
199
+
200
+ def __repr__(self):
201
+ '''
202
+ Return nicely formatted repr string.
203
+ '''
204
+ return "<FixedOffset %r>" % self.__name
205
+
206
+
207
+ class Utc(tz):
208
+ '''UTC
209
+ Universal time coordinated time zone.
210
+ '''
211
+
212
+ def utcoffset(self, dt):
213
+ '''
214
+ Return offset from UTC in minutes east of UTC, which is ZERO for UTC.
215
+ '''
216
+ return ZERO
217
+
218
+ def tzname(self, dt):
219
+ '''
220
+ Return the time zone name corresponding to the datetime object dt,
221
+ as a string.
222
+ '''
223
+ return "UTC"
224
+
225
+ def dst(self, dt):
226
+ '''
227
+ Return the daylight saving time (DST) adjustment, in minutes east
228
+ of UTC.
229
+ '''
230
+ return ZERO
231
+
232
+
233
+ UTC = Utc()
234
+
235
+
236
+ def build_tzinfo(tzname, tzsign='+', tzhour=0, tzmin=0):
237
+ '''
238
+ create a tzinfo instance according to given parameters.
239
+ tzname:
240
+ 'Z' ... return UTC
241
+ '' | None ... return None
242
+ other ... return FixedOffset
243
+ '''
244
+ if tzname is None or tzname == '':
245
+ return None
246
+ if tzname == 'Z':
247
+ return UTC
248
+ tzsign = ((tzsign == '-') and -1) or 1
249
+ return FixedOffset(tzsign * tzhour, tzsign * tzmin, tzname)
250
+
251
+
252
+ def fquotmod(val, low, high):
253
+ a, b = val - low, high - low
254
+ div = (a / b).to_integral(ROUND_FLOOR)
255
+ mod = a - div * b
256
+ mod += low
257
+ return int(div), mod
258
+
259
+
260
+ def max_days_in_month(year, month):
261
+ '''
262
+ Determines the number of days of a specific month in a specific year.
263
+ '''
264
+ if month in (1, 3, 5, 7, 8, 10, 12):
265
+ return 31
266
+ if month in (4, 6, 9, 11):
267
+ return 30
268
+ if ((year % 400) == 0) or ((year % 100) != 0) and ((year % 4) == 0):
269
+ return 29
270
+ return 28
271
+
272
+
273
+ class Duration(object):
274
+ '''
275
+ http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
276
+ '''
277
+
278
+ def __init__(self, days=0, seconds=0, microseconds=0, milliseconds=0,
279
+ minutes=0, hours=0, weeks=0, months=0, years=0):
280
+ '''
281
+ Initialise this Duration instance with the given parameters.
282
+ '''
283
+ if not isinstance(months, Decimal):
284
+ months = Decimal(str(months))
285
+ if not isinstance(years, Decimal):
286
+ years = Decimal(str(years))
287
+ self.months = months
288
+ self.years = years
289
+ self.tdelta = timedelta(days, seconds, microseconds, milliseconds,
290
+ minutes, hours, weeks)
291
+
292
+ def __getstate__(self):
293
+ return self.__dict__
294
+
295
+ def __setstate__(self, state):
296
+ self.__dict__.update(state)
297
+
298
+ def __getattr__(self, name):
299
+
300
+ return getattr(self.tdelta, name)
301
+
302
+ def __str__(self):
303
+
304
+ params = []
305
+ if self.years:
306
+ params.append('%d years' % self.years)
307
+ if self.months:
308
+ fmt = "%d months"
309
+ if self.months <= 1:
310
+ fmt = "%d month"
311
+ params.append(fmt % self.months)
312
+ params.append(str(self.tdelta))
313
+ return ', '.join(params)
314
+
315
+ def __repr__(self):
316
+
317
+ return "%s.%s(%d, %d, %d, years=%d, months=%d)" % (
318
+ self.__class__.__module__, self.__class__.__name__,
319
+ self.tdelta.days, self.tdelta.seconds,
320
+ self.tdelta.microseconds, self.years, self.months)
321
+
322
+ def __hash__(self):
323
+
324
+ return hash((self.tdelta, self.months, self.years))
325
+
326
+ def __neg__(self):
327
+
328
+ negduration = Duration(years=-self.years, months=-self.months)
329
+ negduration.tdelta = -self.tdelta
330
+ return negduration
331
+
332
+ def __add__(self, other):
333
+
334
+ if isinstance(other, Duration):
335
+ newduration = Duration(years=self.years + other.years,
336
+ months=self.months + other.months)
337
+ newduration.tdelta = self.tdelta + other.tdelta
338
+ return newduration
339
+ try:
340
+
341
+ if (not (float(self.years).is_integer() and
342
+ float(self.months).is_integer())):
343
+ raise ValueError('fractional years or months not supported'
344
+ ' for date calculations')
345
+ newmonth = other.month + self.months
346
+ carry, newmonth = fquotmod(newmonth, 1, 13)
347
+ newyear = other.year + self.years + carry
348
+ maxdays = max_days_in_month(newyear, newmonth)
349
+ if other.day > maxdays:
350
+ newday = maxdays
351
+ else:
352
+ newday = other.day
353
+ newdt = other.replace(year=newyear, month=newmonth, day=newday)
354
+
355
+ return self.tdelta + newdt
356
+ except AttributeError:
357
+
358
+ pass
359
+ try:
360
+
361
+ newduration = Duration(years=self.years, months=self.months)
362
+ newduration.tdelta = self.tdelta + other
363
+ return newduration
364
+ except AttributeError:
365
+
366
+ pass
367
+
368
+ return NotImplemented
369
+
370
+ __radd__ = __add__
371
+
372
+ def __mul__(self, other):
373
+ if isinstance(other, int):
374
+ newduration = Duration(
375
+ years=self.years * other,
376
+ months=self.months * other)
377
+ newduration.tdelta = self.tdelta * other
378
+ return newduration
379
+ return NotImplemented
380
+
381
+ __rmul__ = __mul__
382
+
383
+ def __sub__(self, other):
384
+
385
+ if isinstance(other, Duration):
386
+ newduration = Duration(years=self.years - other.years,
387
+ months=self.months - other.months)
388
+ newduration.tdelta = self.tdelta - other.tdelta
389
+ return newduration
390
+ try:
391
+ newduration = Duration(years=self.years, months=self.months)
392
+ newduration.tdelta = self.tdelta - other
393
+ return newduration
394
+ except TypeError:
395
+ pass
396
+ return NotImplemented
397
+
398
+ def __rsub__(self, other):
399
+ if isinstance(other, timedelta):
400
+ tmpdur = Duration()
401
+ tmpdur.tdelta = other
402
+ return tmpdur - self
403
+ try:
404
+ if (not (float(self.years).is_integer() and
405
+ float(self.months).is_integer())):
406
+ raise ValueError('fractional years or months not supported'
407
+ ' for date calculations')
408
+ newmonth = other.month - self.months
409
+ carry, newmonth = fquotmod(newmonth, 1, 13)
410
+ newyear = other.year - self.years + carry
411
+ maxdays = max_days_in_month(newyear, newmonth)
412
+ if other.day > maxdays:
413
+ newday = maxdays
414
+ else:
415
+ newday = other.day
416
+ newdt = other.replace(year=newyear, month=newmonth, day=newday)
417
+ return newdt - self.tdelta
418
+ except AttributeError:
419
+ pass
420
+ return NotImplemented
421
+
422
+ def __eq__(self, other):
423
+
424
+ if isinstance(other, Duration):
425
+ if (((self.years * 12 + self.months) ==
426
+ (other.years * 12 + other.months) and
427
+ self.tdelta == other.tdelta)):
428
+ return True
429
+ return False
430
+ if self.years == 0 and self.months == 0:
431
+ return self.tdelta == other
432
+ return False
433
+
434
+ def __ne__(self, other):
435
+
436
+ if isinstance(other, Duration):
437
+ if (((self.years * 12 + self.months) !=
438
+ (other.years * 12 + other.months) or
439
+ self.tdelta != other.tdelta)):
440
+ return True
441
+ return False
442
+ if self.years == 0 and self.months == 0:
443
+ return self.tdelta != other
444
+ return True
445
+
446
+ def totimedelta(self, start=None, end=None):
447
+ if start is None and end is None:
448
+ raise ValueError("start or end required")
449
+ if start is not None and end is not None:
450
+ raise ValueError("only start or end allowed")
451
+ if start is not None:
452
+ return (start + self) - start
453
+ return end - (end - self)
454
+
455
+
456
+ class ISO8601Error(ValueError):
457
+ '''Raised when the given ISO string can not be parsed.'''
458
+
459
+
460
+ def parse_datetime(datetimestring):
461
+ '''
462
+ Parses ISO 8601 date-times into datetime.datetime objects.
463
+ This function uses parse_date and parse_time to do the job, so it allows
464
+ more combinations of date and time representations, than the actual
465
+ ISO 8601:2004 standard allows.
466
+ '''
467
+ try:
468
+ datestring, timestring = datetimestring.split('T')
469
+ except ValueError:
470
+ raise ISO8601Error("ISO 8601 time designator 'T' missing. Unable to"
471
+ " parse datetime string %r" % datetimestring)
472
+ tmpdate = parse_date(datestring)
473
+ tmptime = parse_time(timestring)
474
+ return datetime.combine(tmpdate, tmptime)
475
+
476
+
477
+ def parse_date(
478
+ datestring,
479
+ yeardigits=4, expanded=False, defaultmonth=1, defaultday=1):
480
+ '''
481
+ Parse an ISO 8601 date string into a datetime.date object.
482
+ As the datetime.date implementation is limited to dates starting from
483
+ 0001-01-01, negative dates (BC) and year 0 can not be parsed by this
484
+ method.
485
+ For incomplete dates, this method chooses the first day for it. For
486
+ instance if only a century is given, this method returns the 1st of
487
+ January in year 1 of this century.
488
+ supported formats: (expanded formats are shown with 6 digits for year)
489
+ YYYYMMDD +-YYYYYYMMDD basic complete date
490
+ YYYY-MM-DD +-YYYYYY-MM-DD extended complete date
491
+ YYYYWwwD +-YYYYYYWwwD basic complete week date
492
+ YYYY-Www-D +-YYYYYY-Www-D extended complete week date
493
+ YYYYDDD +-YYYYYYDDD basic ordinal date
494
+ YYYY-DDD +-YYYYYY-DDD extended ordinal date
495
+ YYYYWww +-YYYYYYWww basic incomplete week date
496
+ YYYY-Www +-YYYYYY-Www extended incomplete week date
497
+ YYYMM +-YYYYYYMM basic incomplete month date
498
+ YYY-MM +-YYYYYY-MM incomplete month date
499
+ YYYY +-YYYYYY incomplete year date
500
+ YY +-YYYY incomplete century date
501
+ @param datestring: the ISO date string to parse
502
+ @param yeardigits: how many digits are used to represent a year
503
+ @param expanded: if True then +/- signs are allowed. This parameter
504
+ is forced to True, if yeardigits != 4
505
+ @return: a datetime.date instance represented by datestring
506
+ @raise ISO8601Error: if this function can not parse the datestring
507
+ @raise ValueError: if datestring can not be represented by datetime.date
508
+ '''
509
+ if yeardigits != 4:
510
+ expanded = True
511
+ isodates = build_date_regexps(yeardigits, expanded)
512
+ for pattern in isodates:
513
+ match = pattern.match(datestring)
514
+ if match:
515
+ groups = match.groupdict()
516
+ # sign, century, year, month, week, day,
517
+ # FIXME: negative dates not possible with python standard types
518
+ sign = (groups['sign'] == '-' and -1) or 1
519
+ if 'century' in groups:
520
+ return date(
521
+ sign * (int(groups['century']) * 100 + 1),
522
+ defaultmonth, defaultday)
523
+ if 'month' not in groups: # weekdate or ordinal date
524
+ ret = date(sign * int(groups['year']), 1, 1)
525
+ if 'week' in groups:
526
+ isotuple = ret.isocalendar()
527
+ if 'day' in groups:
528
+ days = int(groups['day'] or 1)
529
+ else:
530
+ days = 1
531
+ # if first week in year, do weeks-1
532
+ return ret + timedelta(weeks=int(groups['week']) -
533
+ (((isotuple[1] == 1) and 1) or 0),
534
+ days=-isotuple[2] + days)
535
+ elif 'day' in groups: # ordinal date
536
+ return ret + timedelta(days=int(groups['day']) - 1)
537
+ else: # year date
538
+ return ret.replace(month=defaultmonth, day=defaultday)
539
+ # year-, month-, or complete date
540
+ if 'day' not in groups or groups['day'] is None:
541
+ day = defaultday
542
+ else:
543
+ day = int(groups['day'])
544
+ return date(sign * int(groups['year']),
545
+ int(groups['month']) or defaultmonth, day)
546
+ raise ISO8601Error('Unrecognised ISO 8601 date format: %r' % datestring)
547
+
548
+
549
+ def parse_time(timestring):
550
+ '''
551
+ Parses ISO 8601 times into datetime.time objects.
552
+ Following ISO 8601 formats are supported:
553
+ (as decimal separator a ',' or a '.' is allowed)
554
+ hhmmss.ssTZD basic complete time
555
+ hh:mm:ss.ssTZD extended compelte time
556
+ hhmm.mmTZD basic reduced accuracy time
557
+ hh:mm.mmTZD extended reduced accuracy time
558
+ hh.hhTZD basic reduced accuracy time
559
+ TZD is the time zone designator which can be in the following format:
560
+ no designator indicates local time zone
561
+ Z UTC
562
+ +-hhmm basic hours and minutes
563
+ +-hh:mm extended hours and minutes
564
+ +-hh hours
565
+ '''
566
+ isotimes = build_time_regexps()
567
+ for pattern in isotimes:
568
+ match = pattern.match(timestring)
569
+ if match:
570
+ groups = match.groupdict()
571
+ for key, value in groups.items():
572
+ if value is not None:
573
+ groups[key] = value.replace(',', '.')
574
+ Tzinfo = build_tzinfo(groups['tzname'], groups['tzsign'],
575
+ int(groups['tzhour'] or 0),
576
+ int(groups['tzmin'] or 0))
577
+ if 'second' in groups:
578
+ # round to microseconds if fractional seconds are more precise
579
+ second = Decimal(groups['second']).quantize(Decimal('.000001'))
580
+ microsecond = (second - int(second)) * int(1e6)
581
+ # int(...) ... no rounding
582
+ # to_integral() ... rounding
583
+ return time(int(groups['hour']), int(groups['minute']),
584
+ int(second), int(microsecond.to_integral()),
585
+ Tzinfo)
586
+ if 'minute' in groups:
587
+ minute = Decimal(groups['minute'])
588
+ second = (minute - int(minute)) * 60
589
+ microsecond = (second - int(second)) * int(1e6)
590
+ return time(int(groups['hour']), int(minute), int(second),
591
+ int(microsecond.to_integral()), Tzinfo)
592
+ else:
593
+ microsecond, second, minute = 0, 0, 0
594
+ hour = Decimal(groups['hour'])
595
+ minute = (hour - int(hour)) * 60
596
+ second = (minute - int(minute)) * 60
597
+ microsecond = (second - int(second)) * int(1e6)
598
+ return time(int(hour), int(minute), int(second),
599
+ int(microsecond.to_integral()), Tzinfo)
600
+ raise ISO8601Error('Unrecognised ISO 8601 time format: %r' % timestring)