MapProxy 1.16.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mapproxy/__init__.py +0 -0
- mapproxy/cache/__init__.py +36 -0
- mapproxy/cache/azureblob.py +145 -0
- mapproxy/cache/base.py +111 -0
- mapproxy/cache/compact.py +664 -0
- mapproxy/cache/couchdb.py +295 -0
- mapproxy/cache/dummy.py +34 -0
- mapproxy/cache/file.py +185 -0
- mapproxy/cache/geopackage.py +609 -0
- mapproxy/cache/legend.py +83 -0
- mapproxy/cache/mbtiles.py +392 -0
- mapproxy/cache/meta.py +78 -0
- mapproxy/cache/path.py +250 -0
- mapproxy/cache/redis.py +88 -0
- mapproxy/cache/renderd.py +95 -0
- mapproxy/cache/riak.py +202 -0
- mapproxy/cache/s3.py +177 -0
- mapproxy/cache/tile.py +699 -0
- mapproxy/client/__init__.py +0 -0
- mapproxy/client/arcgis.py +79 -0
- mapproxy/client/cgi.py +139 -0
- mapproxy/client/http.py +315 -0
- mapproxy/client/log.py +33 -0
- mapproxy/client/tile.py +150 -0
- mapproxy/client/wms.py +254 -0
- mapproxy/compat/__init__.py +46 -0
- mapproxy/compat/image.py +79 -0
- mapproxy/compat/itertools.py +29 -0
- mapproxy/compat/modules.py +13 -0
- mapproxy/config/__init__.py +22 -0
- mapproxy/config/config.py +201 -0
- mapproxy/config/coverage.py +107 -0
- mapproxy/config/defaults.py +98 -0
- mapproxy/config/loader.py +2286 -0
- mapproxy/config/spec.py +644 -0
- mapproxy/config/validator.py +239 -0
- mapproxy/config_template/__init__.py +0 -0
- mapproxy/config_template/base_config/config.wsgi +10 -0
- mapproxy/config_template/base_config/full_example.yaml +593 -0
- mapproxy/config_template/base_config/full_seed_example.yaml +79 -0
- mapproxy/config_template/base_config/log.ini +35 -0
- mapproxy/config_template/base_config/mapproxy.yaml +60 -0
- mapproxy/config_template/base_config/seed.yaml +27 -0
- mapproxy/exception.py +142 -0
- mapproxy/featureinfo.py +252 -0
- mapproxy/grid.py +1170 -0
- mapproxy/image/__init__.py +536 -0
- mapproxy/image/fonts/DejaVuSans.ttf +0 -0
- mapproxy/image/fonts/DejaVuSansMono.ttf +0 -0
- mapproxy/image/fonts/LICENSE +99 -0
- mapproxy/image/fonts/__init__.py +0 -0
- mapproxy/image/mask.py +75 -0
- mapproxy/image/merge.py +316 -0
- mapproxy/image/message.py +347 -0
- mapproxy/image/opts.py +182 -0
- mapproxy/image/tile.py +167 -0
- mapproxy/image/transform.py +350 -0
- mapproxy/layer.py +470 -0
- mapproxy/multiapp.py +231 -0
- mapproxy/proj.py +302 -0
- mapproxy/request/__init__.py +18 -0
- mapproxy/request/arcgis.py +259 -0
- mapproxy/request/base.py +476 -0
- mapproxy/request/tile.py +128 -0
- mapproxy/request/wms/__init__.py +793 -0
- mapproxy/request/wms/exception.py +99 -0
- mapproxy/request/wmts.py +436 -0
- mapproxy/response.py +237 -0
- mapproxy/script/__init__.py +0 -0
- mapproxy/script/conf/__init__.py +0 -0
- mapproxy/script/conf/app.py +195 -0
- mapproxy/script/conf/caches.py +45 -0
- mapproxy/script/conf/layers.py +54 -0
- mapproxy/script/conf/seeds.py +37 -0
- mapproxy/script/conf/sources.py +86 -0
- mapproxy/script/conf/utils.py +143 -0
- mapproxy/script/defrag.py +184 -0
- mapproxy/script/export.py +333 -0
- mapproxy/script/grids.py +188 -0
- mapproxy/script/scales.py +126 -0
- mapproxy/script/util.py +406 -0
- mapproxy/script/wms_capabilities.py +152 -0
- mapproxy/seed/__init__.py +0 -0
- mapproxy/seed/cachelock.py +121 -0
- mapproxy/seed/cleanup.py +187 -0
- mapproxy/seed/config.py +469 -0
- mapproxy/seed/script.py +388 -0
- mapproxy/seed/seeder.py +538 -0
- mapproxy/seed/spec.py +64 -0
- mapproxy/seed/util.py +254 -0
- mapproxy/service/__init__.py +14 -0
- mapproxy/service/base.py +46 -0
- mapproxy/service/demo.py +356 -0
- mapproxy/service/kml.py +331 -0
- mapproxy/service/ows.py +38 -0
- mapproxy/service/template_helper.py +53 -0
- mapproxy/service/templates/demo/capabilities_demo.html +16 -0
- mapproxy/service/templates/demo/demo.html +181 -0
- mapproxy/service/templates/demo/openlayers-demo.cfg +16 -0
- mapproxy/service/templates/demo/static/img/blank.gif +0 -0
- mapproxy/service/templates/demo/static/img/east-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/north-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/south-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/west-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-minus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-plus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-world-mini.png +0 -0
- mapproxy/service/templates/demo/static/logo.png +0 -0
- mapproxy/service/templates/demo/static/ol.css +345 -0
- mapproxy/service/templates/demo/static/ol.js +4 -0
- mapproxy/service/templates/demo/static/proj4.min.js +1 -0
- mapproxy/service/templates/demo/static/proj4defs.js +1 -0
- mapproxy/service/templates/demo/static/site.css +137 -0
- mapproxy/service/templates/demo/static/theme/default/framedCloud.css +0 -0
- mapproxy/service/templates/demo/static/theme/default/google.css +17 -0
- mapproxy/service/templates/demo/static/theme/default/ie6-style.css +10 -0
- mapproxy/service/templates/demo/static/theme/default/style.css +482 -0
- mapproxy/service/templates/demo/static.html +34 -0
- mapproxy/service/templates/demo/tms_demo.html +103 -0
- mapproxy/service/templates/demo/wms_demo.html +140 -0
- mapproxy/service/templates/demo/wmts_demo.html +110 -0
- mapproxy/service/templates/tms_capabilities.xml +13 -0
- mapproxy/service/templates/tms_exception.xml +4 -0
- mapproxy/service/templates/tms_root_resource.xml +7 -0
- mapproxy/service/templates/tms_tilemap_capabilities.xml +14 -0
- mapproxy/service/templates/wms100capabilities.xml +112 -0
- mapproxy/service/templates/wms100exception.xml +4 -0
- mapproxy/service/templates/wms110capabilities.xml +152 -0
- mapproxy/service/templates/wms110exception.xml +5 -0
- mapproxy/service/templates/wms111capabilities.xml +183 -0
- mapproxy/service/templates/wms111exception.xml +5 -0
- mapproxy/service/templates/wms130capabilities.xml +326 -0
- mapproxy/service/templates/wms130exception.xml +8 -0
- mapproxy/service/templates/wmts100capabilities.xml +155 -0
- mapproxy/service/templates/wmts100exception.xml +9 -0
- mapproxy/service/tile.py +536 -0
- mapproxy/service/wms.py +851 -0
- mapproxy/service/wmts.py +381 -0
- mapproxy/source/__init__.py +75 -0
- mapproxy/source/arcgis.py +39 -0
- mapproxy/source/error.py +39 -0
- mapproxy/source/mapnik.py +259 -0
- mapproxy/source/tile.py +96 -0
- mapproxy/source/wms.py +270 -0
- mapproxy/srs.py +726 -0
- mapproxy/template.py +54 -0
- mapproxy/test/__init__.py +0 -0
- mapproxy/test/conftest.py +7 -0
- mapproxy/test/helper.py +247 -0
- mapproxy/test/http.py +494 -0
- mapproxy/test/image.py +210 -0
- mapproxy/test/mocker.py +2268 -0
- mapproxy/test/schemas/inspire/common/1.0/common.xsd +1461 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_bul.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_cze.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dan.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dut.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_eng.xsd +155 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_est.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fin.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ger.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gle.xsd +109 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_hun.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ita.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lav.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lit.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_mlt.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_pol.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_por.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_rum.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slo.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slv.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_spa.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_swe.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/network.xsd +521 -0
- mapproxy/test/schemas/inspire/inspire_vs/1.0/inspire_vs.xsd +19 -0
- mapproxy/test/schemas/kml/2.2.0/ReadMe.txt +14 -0
- mapproxy/test/schemas/kml/2.2.0/atom-author-link.xsd +66 -0
- mapproxy/test/schemas/kml/2.2.0/ogckml22.xsd +1646 -0
- mapproxy/test/schemas/kml/2.2.0/xAL.xsd +1680 -0
- mapproxy/test/schemas/ows/1.1.0/ReadMe.txt +87 -0
- mapproxy/test/schemas/ows/1.1.0/ows19115subset.xsd +235 -0
- mapproxy/test/schemas/ows/1.1.0/owsAll.xsd +23 -0
- mapproxy/test/schemas/ows/1.1.0/owsCommon.xsd +157 -0
- mapproxy/test/schemas/ows/1.1.0/owsContents.xsd +86 -0
- mapproxy/test/schemas/ows/1.1.0/owsDataIdentification.xsd +127 -0
- mapproxy/test/schemas/ows/1.1.0/owsDomainType.xsd +279 -0
- mapproxy/test/schemas/ows/1.1.0/owsExceptionReport.xsd +76 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetCapabilities.xsd +112 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetResourceByID.xsd +51 -0
- mapproxy/test/schemas/ows/1.1.0/owsInputOutputData.xsd +59 -0
- mapproxy/test/schemas/ows/1.1.0/owsManifest.xsd +125 -0
- mapproxy/test/schemas/ows/1.1.0/owsOperationsMetadata.xsd +140 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceIdentification.xsd +60 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceProvider.xsd +47 -0
- mapproxy/test/schemas/sld/1.1.0/sld_capabilities.xsd +27 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.dtd +353 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.xml +188 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.dtd +524 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.xml +260 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.dtd +273 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.xml +303 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.xml +33 -0
- mapproxy/test/schemas/wms/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd +274 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wms/1.3.0/ReadMe.txt +8 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xml +277 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xsd +611 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xml +34 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xsd +28 -0
- mapproxy/test/schemas/wmsc/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_MS_Capabilities.dtd +283 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wmts/1.0/ReadMe.txt +32 -0
- mapproxy/test/schemas/wmts/1.0/wmts.xsd +28 -0
- mapproxy/test/schemas/wmts/1.0/wmtsAbstract.wsdl +151 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_request.xsd +38 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_response.xsd +564 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_request.xsd +57 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_response.xsd +72 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetTile_request.xsd +91 -0
- mapproxy/test/schemas/wmts/1.0/wmtsKVP.xsd +76 -0
- mapproxy/test/schemas/wmts/1.0/wmtsPayload_response.xsd +70 -0
- mapproxy/test/schemas/xlink/1.0.0/ReadMe.txt +6 -0
- mapproxy/test/schemas/xlink/1.0.0/xlinks.xsd +122 -0
- mapproxy/test/schemas/xml.xsd +287 -0
- mapproxy/test/system/__init__.py +98 -0
- mapproxy/test/system/fixture/arcgis.yaml +57 -0
- mapproxy/test/system/fixture/auth.yaml +70 -0
- mapproxy/test/system/fixture/cache.mbtiles +0 -0
- mapproxy/test/system/fixture/cache_azureblob.yaml +59 -0
- mapproxy/test/system/fixture/cache_band_merge.yaml +73 -0
- mapproxy/test/system/fixture/cache_bulk_meta_tiles.yaml +24 -0
- mapproxy/test/system/fixture/cache_data/dop_cache_EPSG3857/00/000/000/000/000/000/000.png +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_EPSG900913/01/000/000/000/000/000/001.jpeg +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_transparent_EPSG900913/01/000/000/000/000/000/001.png +0 -0
- mapproxy/test/system/fixture/cache_geopackage.yaml +56 -0
- mapproxy/test/system/fixture/cache_grid_names.yaml +50 -0
- mapproxy/test/system/fixture/cache_mbtiles.yaml +28 -0
- mapproxy/test/system/fixture/cache_s3.yaml +58 -0
- mapproxy/test/system/fixture/cache_source.yaml +81 -0
- mapproxy/test/system/fixture/combined_sources.yaml +130 -0
- mapproxy/test/system/fixture/coverage.yaml +77 -0
- mapproxy/test/system/fixture/demo.yaml +135 -0
- mapproxy/test/system/fixture/dimension.yaml +59 -0
- mapproxy/test/system/fixture/disable_storage.yaml +25 -0
- mapproxy/test/system/fixture/empty_ogrdata.geojson +1 -0
- mapproxy/test/system/fixture/formats.yaml +72 -0
- mapproxy/test/system/fixture/inspire.yaml +101 -0
- mapproxy/test/system/fixture/inspire_full.yaml +124 -0
- mapproxy/test/system/fixture/kml_layer.yaml +66 -0
- mapproxy/test/system/fixture/layer.yaml +260 -0
- mapproxy/test/system/fixture/layergroups.yaml +57 -0
- mapproxy/test/system/fixture/layergroups_root.yaml +106 -0
- mapproxy/test/system/fixture/legendgraphic.yaml +93 -0
- mapproxy/test/system/fixture/mapnik_source.yaml +66 -0
- mapproxy/test/system/fixture/mapproxy_export.yaml +12 -0
- mapproxy/test/system/fixture/mapserver.yaml +23 -0
- mapproxy/test/system/fixture/minimal_cgi.py +16 -0
- mapproxy/test/system/fixture/mixed_mode.yaml +49 -0
- mapproxy/test/system/fixture/multi_cache_layers.yaml +100 -0
- mapproxy/test/system/fixture/multiapp1.yaml +20 -0
- mapproxy/test/system/fixture/multiapp2.yaml +19 -0
- mapproxy/test/system/fixture/renderd_client.yaml +55 -0
- mapproxy/test/system/fixture/scalehints.yaml +70 -0
- mapproxy/test/system/fixture/seed.yaml +94 -0
- mapproxy/test/system/fixture/seed_mapproxy.yaml +39 -0
- mapproxy/test/system/fixture/seed_old.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts_mapproxy.yaml +27 -0
- mapproxy/test/system/fixture/seedonly.yaml +51 -0
- mapproxy/test/system/fixture/sld.yaml +35 -0
- mapproxy/test/system/fixture/source_errors.yaml +84 -0
- mapproxy/test/system/fixture/source_errors_raise.yaml +82 -0
- mapproxy/test/system/fixture/tileservice_origin.yaml +26 -0
- mapproxy/test/system/fixture/tileservice_refresh.yaml +59 -0
- mapproxy/test/system/fixture/tilesource_minmax_res.yaml +22 -0
- mapproxy/test/system/fixture/util-conf-base-grids.yaml +5 -0
- mapproxy/test/system/fixture/util-conf-overwrite.yaml +13 -0
- mapproxy/test/system/fixture/util-conf-wms-111-cap.xml +90 -0
- mapproxy/test/system/fixture/util_grids.yaml +30 -0
- mapproxy/test/system/fixture/util_wms_capabilities111.xml +130 -0
- mapproxy/test/system/fixture/util_wms_capabilities130.xml +100 -0
- mapproxy/test/system/fixture/util_wms_capabilities_service_exception.xml +5 -0
- mapproxy/test/system/fixture/watermark.yaml +50 -0
- mapproxy/test/system/fixture/wms_srs_extent.yaml +39 -0
- mapproxy/test/system/fixture/wms_versions.yaml +38 -0
- mapproxy/test/system/fixture/wmts.yaml +134 -0
- mapproxy/test/system/fixture/wmts_dimensions.yaml +57 -0
- mapproxy/test/system/fixture/xslt_featureinfo.yaml +54 -0
- mapproxy/test/system/fixture/xslt_featureinfo_input.yaml +51 -0
- mapproxy/test/system/test_arcgis.py +156 -0
- mapproxy/test/system/test_auth.py +1134 -0
- mapproxy/test/system/test_behind_proxy.py +75 -0
- mapproxy/test/system/test_bulk_meta_tiles.py +106 -0
- mapproxy/test/system/test_cache_azureblob.py +127 -0
- mapproxy/test/system/test_cache_band_merge.py +103 -0
- mapproxy/test/system/test_cache_geopackage.py +144 -0
- mapproxy/test/system/test_cache_grid_names.py +89 -0
- mapproxy/test/system/test_cache_mbtiles.py +85 -0
- mapproxy/test/system/test_cache_s3.py +115 -0
- mapproxy/test/system/test_cache_source.py +146 -0
- mapproxy/test/system/test_combined_sources.py +335 -0
- mapproxy/test/system/test_coverage.py +140 -0
- mapproxy/test/system/test_decorate_img.py +214 -0
- mapproxy/test/system/test_demo.py +106 -0
- mapproxy/test/system/test_demo_with_extra_service.py +53 -0
- mapproxy/test/system/test_dimensions.py +278 -0
- mapproxy/test/system/test_disable_storage.py +42 -0
- mapproxy/test/system/test_formats.py +219 -0
- mapproxy/test/system/test_inspire_vs.py +173 -0
- mapproxy/test/system/test_kml.py +262 -0
- mapproxy/test/system/test_layergroups.py +160 -0
- mapproxy/test/system/test_legendgraphic.py +308 -0
- mapproxy/test/system/test_mapnik.py +161 -0
- mapproxy/test/system/test_mapserver.py +81 -0
- mapproxy/test/system/test_mixed_mode_format.py +195 -0
- mapproxy/test/system/test_multi_cache_layers.py +167 -0
- mapproxy/test/system/test_multiapp.py +92 -0
- mapproxy/test/system/test_refresh.py +207 -0
- mapproxy/test/system/test_renderd_client.py +304 -0
- mapproxy/test/system/test_response_headers.py +54 -0
- mapproxy/test/system/test_scalehints.py +140 -0
- mapproxy/test/system/test_seed.py +422 -0
- mapproxy/test/system/test_seed_only.py +93 -0
- mapproxy/test/system/test_sld.py +120 -0
- mapproxy/test/system/test_source_errors.py +377 -0
- mapproxy/test/system/test_tilesource_minmax_res.py +54 -0
- mapproxy/test/system/test_tms.py +276 -0
- mapproxy/test/system/test_tms_origin.py +46 -0
- mapproxy/test/system/test_util_conf.py +304 -0
- mapproxy/test/system/test_util_export.py +210 -0
- mapproxy/test/system/test_util_grids.py +88 -0
- mapproxy/test/system/test_util_wms_capabilities.py +182 -0
- mapproxy/test/system/test_watermark.py +91 -0
- mapproxy/test/system/test_wms.py +1611 -0
- mapproxy/test/system/test_wms_srs_extent.py +165 -0
- mapproxy/test/system/test_wms_version.py +85 -0
- mapproxy/test/system/test_wmsc.py +116 -0
- mapproxy/test/system/test_wmts.py +334 -0
- mapproxy/test/system/test_wmts_dimensions.py +206 -0
- mapproxy/test/system/test_wmts_restful.py +198 -0
- mapproxy/test/system/test_xslt_featureinfo.py +425 -0
- mapproxy/test/test_http_helper.py +219 -0
- mapproxy/test/unit/__init__.py +0 -0
- mapproxy/test/unit/epsg +2 -0
- mapproxy/test/unit/polygons/polygons.dbf +0 -0
- mapproxy/test/unit/polygons/polygons.shp +0 -0
- mapproxy/test/unit/polygons/polygons.shx +0 -0
- mapproxy/test/unit/test_async.py +245 -0
- mapproxy/test/unit/test_auth.py +419 -0
- mapproxy/test/unit/test_cache.py +1193 -0
- mapproxy/test/unit/test_cache_azureblob.py +94 -0
- mapproxy/test/unit/test_cache_compact.py +319 -0
- mapproxy/test/unit/test_cache_couchdb.py +114 -0
- mapproxy/test/unit/test_cache_geopackage.py +221 -0
- mapproxy/test/unit/test_cache_redis.py +67 -0
- mapproxy/test/unit/test_cache_riak.py +76 -0
- mapproxy/test/unit/test_cache_s3.py +84 -0
- mapproxy/test/unit/test_cache_tile.py +427 -0
- mapproxy/test/unit/test_client.py +479 -0
- mapproxy/test/unit/test_client_arcgis.py +73 -0
- mapproxy/test/unit/test_client_cgi.py +136 -0
- mapproxy/test/unit/test_collections.py +116 -0
- mapproxy/test/unit/test_concat_legends.py +37 -0
- mapproxy/test/unit/test_conf_loader.py +1061 -0
- mapproxy/test/unit/test_conf_validator.py +416 -0
- mapproxy/test/unit/test_config.py +117 -0
- mapproxy/test/unit/test_decorate_img.py +185 -0
- mapproxy/test/unit/test_exceptions.py +258 -0
- mapproxy/test/unit/test_featureinfo.py +291 -0
- mapproxy/test/unit/test_file_lock_load.py +49 -0
- mapproxy/test/unit/test_geom.py +503 -0
- mapproxy/test/unit/test_grid.py +1258 -0
- mapproxy/test/unit/test_image.py +1053 -0
- mapproxy/test/unit/test_image_mask.py +181 -0
- mapproxy/test/unit/test_image_messages.py +197 -0
- mapproxy/test/unit/test_image_options.py +160 -0
- mapproxy/test/unit/test_isodate.py +122 -0
- mapproxy/test/unit/test_multiapp.py +163 -0
- mapproxy/test/unit/test_ogr_reader.py +50 -0
- mapproxy/test/unit/test_request.py +745 -0
- mapproxy/test/unit/test_request_wmts.py +178 -0
- mapproxy/test/unit/test_response.py +79 -0
- mapproxy/test/unit/test_seed.py +365 -0
- mapproxy/test/unit/test_seed_cachelock.py +90 -0
- mapproxy/test/unit/test_srs.py +215 -0
- mapproxy/test/unit/test_tiled_source.py +122 -0
- mapproxy/test/unit/test_tilefilter.py +31 -0
- mapproxy/test/unit/test_times.py +25 -0
- mapproxy/test/unit/test_timeutils.py +50 -0
- mapproxy/test/unit/test_util_conf_utils.py +75 -0
- mapproxy/test/unit/test_utils.py +476 -0
- mapproxy/test/unit/test_wms_capabilities.py +44 -0
- mapproxy/test/unit/test_wms_layer.py +113 -0
- mapproxy/test/unit/test_yaml.py +69 -0
- mapproxy/tilefilter.py +59 -0
- mapproxy/util/__init__.py +0 -0
- mapproxy/util/async_.py +227 -0
- mapproxy/util/collections.py +132 -0
- mapproxy/util/coverage.py +329 -0
- mapproxy/util/escape.py +10 -0
- mapproxy/util/ext/__init__.py +14 -0
- mapproxy/util/ext/dictspec/__init__.py +1 -0
- mapproxy/util/ext/dictspec/spec.py +124 -0
- mapproxy/util/ext/dictspec/test/__init__.py +0 -0
- mapproxy/util/ext/dictspec/test/test_validator.py +274 -0
- mapproxy/util/ext/dictspec/validator.py +189 -0
- mapproxy/util/ext/local.py +196 -0
- mapproxy/util/ext/lockfile.py +138 -0
- mapproxy/util/ext/odict.py +330 -0
- mapproxy/util/ext/serving.py +508 -0
- mapproxy/util/ext/tempita/__init__.py +1174 -0
- mapproxy/util/ext/tempita/_looper.py +163 -0
- mapproxy/util/ext/tempita/compat3.py +46 -0
- mapproxy/util/ext/wmsparse/__init__.py +3 -0
- mapproxy/util/ext/wmsparse/duration.py +597 -0
- mapproxy/util/ext/wmsparse/parse.py +305 -0
- mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
- mapproxy/util/ext/wmsparse/test/test_parse.py +162 -0
- mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
- mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
- mapproxy/util/ext/wmsparse/test/wms-omniscale-111.xml +90 -0
- mapproxy/util/ext/wmsparse/test/wms-omniscale-130.xml +120 -0
- mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
- mapproxy/util/ext/wmsparse/util.py +187 -0
- mapproxy/util/fs.py +156 -0
- mapproxy/util/geom.py +295 -0
- mapproxy/util/lib.py +115 -0
- mapproxy/util/lock.py +163 -0
- mapproxy/util/ogr.py +231 -0
- mapproxy/util/py.py +81 -0
- mapproxy/util/times.py +75 -0
- mapproxy/util/yaml.py +56 -0
- mapproxy/version.py +31 -0
- mapproxy/wsgiapp.py +164 -0
- mapproxy-1.16.1.dist-info/METADATA +151 -0
- mapproxy-1.16.1.dist-info/RECORD +458 -0
- mapproxy-1.16.1.dist-info/WHEEL +5 -0
- mapproxy-1.16.1.dist-info/entry_points.txt +3 -0
- mapproxy-1.16.1.dist-info/licenses/AUTHORS.txt +33 -0
- mapproxy-1.16.1.dist-info/licenses/COPYING.txt +60 -0
- mapproxy-1.16.1.dist-info/licenses/LICENSE.txt +202 -0
- mapproxy-1.16.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A small templating language
|
|
3
|
+
|
|
4
|
+
This implements a small templating language. This language implements
|
|
5
|
+
if/elif/else, for/continue/break, expressions, and blocks of Python
|
|
6
|
+
code. The syntax is::
|
|
7
|
+
|
|
8
|
+
{{any expression (function calls etc)}}
|
|
9
|
+
{{any expression | filter}}
|
|
10
|
+
{{for x in y}}...{{endfor}}
|
|
11
|
+
{{if x}}x{{elif y}}y{{else}}z{{endif}}
|
|
12
|
+
{{py:x=1}}
|
|
13
|
+
{{py:
|
|
14
|
+
def foo(bar):
|
|
15
|
+
return 'baz'
|
|
16
|
+
}}
|
|
17
|
+
{{default var = default_value}}
|
|
18
|
+
{{# comment}}
|
|
19
|
+
|
|
20
|
+
You use this with the ``Template`` class or the ``sub`` shortcut.
|
|
21
|
+
The ``Template`` class takes the template string and the name of
|
|
22
|
+
the template (for errors) and a default namespace. Then (like
|
|
23
|
+
``string.Template``) you can call the ``tmpl.substitute(**kw)``
|
|
24
|
+
method to make a substitution (or ``tmpl.substitute(a_dict)``).
|
|
25
|
+
|
|
26
|
+
``sub(content, **kw)`` substitutes the template immediately. You
|
|
27
|
+
can use ``__name='tmpl.html'`` to set the name of the template.
|
|
28
|
+
|
|
29
|
+
If there are syntax errors ``TemplateError`` will be raised.
|
|
30
|
+
"""
|
|
31
|
+
from __future__ import print_function
|
|
32
|
+
|
|
33
|
+
import re
|
|
34
|
+
import sys
|
|
35
|
+
import os
|
|
36
|
+
import tokenize
|
|
37
|
+
from io import StringIO, BytesIO
|
|
38
|
+
from mapproxy.compat import iteritems, PY2, text_type
|
|
39
|
+
from mapproxy.compat.modules import escape
|
|
40
|
+
from mapproxy.util.py import reraise
|
|
41
|
+
from mapproxy.util.ext.tempita._looper import looper
|
|
42
|
+
from mapproxy.util.ext.tempita.compat3 import bytes, basestring_, next, is_unicode, coerce_text
|
|
43
|
+
|
|
44
|
+
if PY2:
|
|
45
|
+
from urllib import quote as url_quote
|
|
46
|
+
else:
|
|
47
|
+
from urllib.parse import quote as url_quote
|
|
48
|
+
|
|
49
|
+
__all__ = ['TemplateError', 'Template', 'sub', 'HTMLTemplate',
|
|
50
|
+
'sub_html', 'html', 'bunch']
|
|
51
|
+
|
|
52
|
+
token_re = re.compile(r'\{\{|\}\}')
|
|
53
|
+
in_re = re.compile(r'\s+in\s+')
|
|
54
|
+
var_re = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TemplateError(Exception):
|
|
58
|
+
"""Exception raised while parsing a template
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, message, position, name=None):
|
|
62
|
+
Exception.__init__(self, message)
|
|
63
|
+
self.position = position
|
|
64
|
+
self.name = name
|
|
65
|
+
|
|
66
|
+
def __str__(self):
|
|
67
|
+
msg = ' '.join(self.args)
|
|
68
|
+
if self.position:
|
|
69
|
+
msg = '%s at line %s column %s' % (
|
|
70
|
+
msg, self.position[0], self.position[1])
|
|
71
|
+
if self.name:
|
|
72
|
+
msg += ' in %s' % self.name
|
|
73
|
+
return msg
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class _TemplateContinue(Exception):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class _TemplateBreak(Exception):
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_file_template(name, from_template):
|
|
85
|
+
path = os.path.join(os.path.dirname(from_template.name), name)
|
|
86
|
+
return from_template.__class__.from_filename(
|
|
87
|
+
path, namespace=from_template.namespace,
|
|
88
|
+
get_template=from_template.get_template)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Template(object):
|
|
92
|
+
|
|
93
|
+
default_namespace = {
|
|
94
|
+
'start_braces': '{{',
|
|
95
|
+
'end_braces': '}}',
|
|
96
|
+
'looper': looper,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
default_encoding = 'utf8'
|
|
100
|
+
default_inherit = None
|
|
101
|
+
|
|
102
|
+
def __init__(self, content, name=None, namespace=None, stacklevel=None,
|
|
103
|
+
get_template=None, default_inherit=None, line_offset=0):
|
|
104
|
+
self.content = content
|
|
105
|
+
self._unicode = is_unicode(content)
|
|
106
|
+
if name is None and stacklevel is not None:
|
|
107
|
+
try:
|
|
108
|
+
caller = sys._getframe(stacklevel)
|
|
109
|
+
except ValueError:
|
|
110
|
+
pass
|
|
111
|
+
else:
|
|
112
|
+
globals = caller.f_globals
|
|
113
|
+
lineno = caller.f_lineno
|
|
114
|
+
if '__file__' in globals:
|
|
115
|
+
name = globals['__file__']
|
|
116
|
+
if name.endswith('.pyc') or name.endswith('.pyo'):
|
|
117
|
+
name = name[:-1]
|
|
118
|
+
elif '__name__' in globals:
|
|
119
|
+
name = globals['__name__']
|
|
120
|
+
else:
|
|
121
|
+
name = '<string>'
|
|
122
|
+
if lineno:
|
|
123
|
+
name += ':%s' % lineno
|
|
124
|
+
self.name = name
|
|
125
|
+
self._parsed = parse(content, name=name, line_offset=line_offset)
|
|
126
|
+
if namespace is None:
|
|
127
|
+
namespace = {}
|
|
128
|
+
self.namespace = namespace
|
|
129
|
+
self.get_template = get_template
|
|
130
|
+
if default_inherit is not None:
|
|
131
|
+
self.default_inherit = default_inherit
|
|
132
|
+
|
|
133
|
+
def from_filename(cls, filename, namespace=None, encoding=None,
|
|
134
|
+
default_inherit=None, get_template=get_file_template):
|
|
135
|
+
f = open(filename, 'rb')
|
|
136
|
+
c = f.read()
|
|
137
|
+
f.close()
|
|
138
|
+
if encoding:
|
|
139
|
+
c = c.decode(encoding)
|
|
140
|
+
return cls(content=c, name=filename, namespace=namespace,
|
|
141
|
+
default_inherit=default_inherit, get_template=get_template)
|
|
142
|
+
|
|
143
|
+
from_filename = classmethod(from_filename)
|
|
144
|
+
|
|
145
|
+
def __repr__(self):
|
|
146
|
+
return '<%s %s name=%r>' % (
|
|
147
|
+
self.__class__.__name__,
|
|
148
|
+
hex(id(self))[2:], self.name)
|
|
149
|
+
|
|
150
|
+
def substitute(self, *args, **kw):
|
|
151
|
+
if args:
|
|
152
|
+
if kw:
|
|
153
|
+
raise TypeError(
|
|
154
|
+
"You can only give positional *or* keyword arguments")
|
|
155
|
+
if len(args) > 1:
|
|
156
|
+
raise TypeError(
|
|
157
|
+
"You can only give one positional argument")
|
|
158
|
+
if not hasattr(args[0], 'items'):
|
|
159
|
+
raise TypeError(
|
|
160
|
+
"If you pass in a single argument, you must pass in a dictionary-like object (with a .items() method); you gave %r"
|
|
161
|
+
% (args[0],))
|
|
162
|
+
kw = args[0]
|
|
163
|
+
ns = kw
|
|
164
|
+
ns['__template_name__'] = self.name
|
|
165
|
+
if self.namespace:
|
|
166
|
+
ns.update(self.namespace)
|
|
167
|
+
result, defs, inherit = self._interpret(ns)
|
|
168
|
+
if not inherit:
|
|
169
|
+
inherit = self.default_inherit
|
|
170
|
+
if inherit:
|
|
171
|
+
result = self._interpret_inherit(result, defs, inherit, ns)
|
|
172
|
+
return result
|
|
173
|
+
|
|
174
|
+
def _interpret(self, ns):
|
|
175
|
+
__traceback_hide__ = True
|
|
176
|
+
parts = []
|
|
177
|
+
defs = {}
|
|
178
|
+
self._interpret_codes(self._parsed, ns, out=parts, defs=defs)
|
|
179
|
+
if '__inherit__' in defs:
|
|
180
|
+
inherit = defs.pop('__inherit__')
|
|
181
|
+
else:
|
|
182
|
+
inherit = None
|
|
183
|
+
return ''.join(parts), defs, inherit
|
|
184
|
+
|
|
185
|
+
def _interpret_inherit(self, body, defs, inherit_template, ns):
|
|
186
|
+
__traceback_hide__ = True
|
|
187
|
+
if not self.get_template:
|
|
188
|
+
raise TemplateError(
|
|
189
|
+
'You cannot use inheritance without passing in get_template',
|
|
190
|
+
position=None, name=self.name)
|
|
191
|
+
templ = self.get_template(inherit_template, self)
|
|
192
|
+
self_ = TemplateObject(self.name)
|
|
193
|
+
for name, value in iteritems(defs):
|
|
194
|
+
setattr(self_, name, value)
|
|
195
|
+
self_.body = body
|
|
196
|
+
ns = ns.copy()
|
|
197
|
+
ns['self'] = self_
|
|
198
|
+
return templ.substitute(ns)
|
|
199
|
+
|
|
200
|
+
def _interpret_codes(self, codes, ns, out, defs):
|
|
201
|
+
__traceback_hide__ = True
|
|
202
|
+
for item in codes:
|
|
203
|
+
if isinstance(item, basestring_):
|
|
204
|
+
out.append(item)
|
|
205
|
+
else:
|
|
206
|
+
self._interpret_code(item, ns, out, defs)
|
|
207
|
+
|
|
208
|
+
def _interpret_code(self, code, ns, out, defs):
|
|
209
|
+
__traceback_hide__ = True
|
|
210
|
+
name, pos = code[0], code[1]
|
|
211
|
+
if name == 'py':
|
|
212
|
+
self._exec(code[2], ns, pos)
|
|
213
|
+
elif name == 'continue':
|
|
214
|
+
raise _TemplateContinue()
|
|
215
|
+
elif name == 'break':
|
|
216
|
+
raise _TemplateBreak()
|
|
217
|
+
elif name == 'for':
|
|
218
|
+
vars, expr, content = code[2], code[3], code[4]
|
|
219
|
+
expr = self._eval(expr, ns, pos)
|
|
220
|
+
self._interpret_for(vars, expr, content, ns, out, defs)
|
|
221
|
+
elif name == 'cond':
|
|
222
|
+
parts = code[2:]
|
|
223
|
+
self._interpret_if(parts, ns, out, defs)
|
|
224
|
+
elif name == 'expr':
|
|
225
|
+
parts = code[2].split('|')
|
|
226
|
+
base = self._eval(parts[0], ns, pos)
|
|
227
|
+
for part in parts[1:]:
|
|
228
|
+
func = self._eval(part, ns, pos)
|
|
229
|
+
base = func(base)
|
|
230
|
+
out.append(self._repr(base, pos))
|
|
231
|
+
elif name == 'default':
|
|
232
|
+
var, expr = code[2], code[3]
|
|
233
|
+
if var not in ns:
|
|
234
|
+
result = self._eval(expr, ns, pos)
|
|
235
|
+
ns[var] = result
|
|
236
|
+
elif name == 'inherit':
|
|
237
|
+
expr = code[2]
|
|
238
|
+
value = self._eval(expr, ns, pos)
|
|
239
|
+
defs['__inherit__'] = value
|
|
240
|
+
elif name == 'def':
|
|
241
|
+
name = code[2]
|
|
242
|
+
signature = code[3]
|
|
243
|
+
parts = code[4]
|
|
244
|
+
ns[name] = defs[name] = TemplateDef(self, name, signature, body=parts, ns=ns,
|
|
245
|
+
pos=pos)
|
|
246
|
+
elif name == 'comment':
|
|
247
|
+
return
|
|
248
|
+
else:
|
|
249
|
+
assert 0, "Unknown code: %r" % name
|
|
250
|
+
|
|
251
|
+
def _interpret_for(self, vars, expr, content, ns, out, defs):
|
|
252
|
+
__traceback_hide__ = True
|
|
253
|
+
if expr is None:
|
|
254
|
+
return
|
|
255
|
+
for item in expr:
|
|
256
|
+
if len(vars) == 1:
|
|
257
|
+
ns[vars[0]] = item
|
|
258
|
+
else:
|
|
259
|
+
if len(vars) != len(item):
|
|
260
|
+
raise ValueError(
|
|
261
|
+
'Need %i items to unpack (got %i items)'
|
|
262
|
+
% (len(vars), len(item)))
|
|
263
|
+
for name, value in zip(vars, item):
|
|
264
|
+
ns[name] = value
|
|
265
|
+
try:
|
|
266
|
+
self._interpret_codes(content, ns, out, defs)
|
|
267
|
+
except _TemplateContinue:
|
|
268
|
+
continue
|
|
269
|
+
except _TemplateBreak:
|
|
270
|
+
break
|
|
271
|
+
|
|
272
|
+
def _interpret_if(self, parts, ns, out, defs):
|
|
273
|
+
__traceback_hide__ = True
|
|
274
|
+
# @@: if/else/else gets through
|
|
275
|
+
for part in parts:
|
|
276
|
+
assert not isinstance(part, basestring_)
|
|
277
|
+
name, pos = part[0], part[1]
|
|
278
|
+
if name == 'else':
|
|
279
|
+
result = True
|
|
280
|
+
else:
|
|
281
|
+
result = self._eval(part[2], ns, pos)
|
|
282
|
+
if result:
|
|
283
|
+
self._interpret_codes(part[3], ns, out, defs)
|
|
284
|
+
break
|
|
285
|
+
|
|
286
|
+
def _eval(self, code, ns, pos):
|
|
287
|
+
__traceback_hide__ = True
|
|
288
|
+
try:
|
|
289
|
+
try:
|
|
290
|
+
value = eval(code, self.default_namespace, ns)
|
|
291
|
+
except SyntaxError as e:
|
|
292
|
+
raise SyntaxError(
|
|
293
|
+
'invalid syntax in expression: %s' % code)
|
|
294
|
+
return value
|
|
295
|
+
except:
|
|
296
|
+
exc_info = sys.exc_info()
|
|
297
|
+
e = exc_info[1]
|
|
298
|
+
if getattr(e, 'args', None):
|
|
299
|
+
arg0 = e.args[0]
|
|
300
|
+
else:
|
|
301
|
+
arg0 = coerce_text(e)
|
|
302
|
+
e.args = (self._add_line_info(arg0, pos),)
|
|
303
|
+
reraise((exc_info[0], e, exc_info[2]))
|
|
304
|
+
|
|
305
|
+
def _exec(self, code, ns, pos):
|
|
306
|
+
__traceback_hide__ = True
|
|
307
|
+
try:
|
|
308
|
+
exec(code, self.default_namespace, ns)
|
|
309
|
+
except:
|
|
310
|
+
exc_info = sys.exc_info()
|
|
311
|
+
e = exc_info[1]
|
|
312
|
+
if e.args:
|
|
313
|
+
e.args = (self._add_line_info(e.args[0], pos),)
|
|
314
|
+
else:
|
|
315
|
+
e.args = (self._add_line_info(None, pos),)
|
|
316
|
+
reraise((exc_info[0], e, exc_info[2]))
|
|
317
|
+
|
|
318
|
+
def _repr(self, value, pos):
|
|
319
|
+
__traceback_hide__ = True
|
|
320
|
+
try:
|
|
321
|
+
if value is None:
|
|
322
|
+
return ''
|
|
323
|
+
if self._unicode:
|
|
324
|
+
try:
|
|
325
|
+
value = text_type(value)
|
|
326
|
+
except UnicodeDecodeError:
|
|
327
|
+
value = bytes(value)
|
|
328
|
+
else:
|
|
329
|
+
if not isinstance(value, basestring_):
|
|
330
|
+
value = coerce_text(value)
|
|
331
|
+
if (is_unicode(value)
|
|
332
|
+
and self.default_encoding):
|
|
333
|
+
value = value.encode(self.default_encoding)
|
|
334
|
+
except:
|
|
335
|
+
exc_info = sys.exc_info()
|
|
336
|
+
e = exc_info[1]
|
|
337
|
+
e.args = (self._add_line_info(e.args[0], pos),)
|
|
338
|
+
reraise((exc_info[0], e, exc_info[2]))
|
|
339
|
+
else:
|
|
340
|
+
if self._unicode and isinstance(value, bytes):
|
|
341
|
+
if not self.default_encoding:
|
|
342
|
+
raise UnicodeDecodeError(
|
|
343
|
+
'Cannot decode bytes value %r into unicode '
|
|
344
|
+
'(no default_encoding provided)' % value)
|
|
345
|
+
try:
|
|
346
|
+
value = value.decode(self.default_encoding)
|
|
347
|
+
except UnicodeDecodeError as e:
|
|
348
|
+
raise UnicodeDecodeError(
|
|
349
|
+
e.encoding,
|
|
350
|
+
e.object,
|
|
351
|
+
e.start,
|
|
352
|
+
e.end,
|
|
353
|
+
e.reason + ' in string %r' % value)
|
|
354
|
+
elif not self._unicode and is_unicode(value):
|
|
355
|
+
if not self.default_encoding:
|
|
356
|
+
raise UnicodeEncodeError(
|
|
357
|
+
'Cannot encode unicode value %r into bytes '
|
|
358
|
+
'(no default_encoding provided)' % value)
|
|
359
|
+
value = value.encode(self.default_encoding)
|
|
360
|
+
return value
|
|
361
|
+
|
|
362
|
+
def _add_line_info(self, msg, pos):
|
|
363
|
+
msg = "%s at line %s column %s" % (
|
|
364
|
+
msg, pos[0], pos[1])
|
|
365
|
+
if self.name:
|
|
366
|
+
msg += " in file %s" % self.name
|
|
367
|
+
return msg
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def sub(content, **kw):
|
|
371
|
+
name = kw.get('__name')
|
|
372
|
+
tmpl = Template(content, name=name)
|
|
373
|
+
return tmpl.substitute(kw)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def paste_script_template_renderer(content, vars, filename=None):
|
|
377
|
+
tmpl = Template(content, name=filename)
|
|
378
|
+
return tmpl.substitute(vars)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class bunch(dict):
|
|
382
|
+
|
|
383
|
+
def __init__(self, **kw):
|
|
384
|
+
for name, value in iteritems(kw):
|
|
385
|
+
setattr(self, name, value)
|
|
386
|
+
|
|
387
|
+
def __setattr__(self, name, value):
|
|
388
|
+
self[name] = value
|
|
389
|
+
|
|
390
|
+
def __getattr__(self, name):
|
|
391
|
+
try:
|
|
392
|
+
return self[name]
|
|
393
|
+
except KeyError:
|
|
394
|
+
raise AttributeError(name)
|
|
395
|
+
|
|
396
|
+
def __getitem__(self, key):
|
|
397
|
+
if 'default' in self:
|
|
398
|
+
try:
|
|
399
|
+
return dict.__getitem__(self, key)
|
|
400
|
+
except KeyError:
|
|
401
|
+
return dict.__getitem__(self, 'default')
|
|
402
|
+
else:
|
|
403
|
+
return dict.__getitem__(self, key)
|
|
404
|
+
|
|
405
|
+
def __repr__(self):
|
|
406
|
+
items = [
|
|
407
|
+
(k, v) for k, v in iteritems(self)]
|
|
408
|
+
items.sort()
|
|
409
|
+
return '<%s %s>' % (
|
|
410
|
+
self.__class__.__name__,
|
|
411
|
+
' '.join(['%s=%r' % (k, v) for k, v in items]))
|
|
412
|
+
|
|
413
|
+
############################################################
|
|
414
|
+
## HTML Templating
|
|
415
|
+
############################################################
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
class html(object):
|
|
419
|
+
|
|
420
|
+
def __init__(self, value):
|
|
421
|
+
self.value = value
|
|
422
|
+
|
|
423
|
+
def __str__(self):
|
|
424
|
+
return self.value
|
|
425
|
+
|
|
426
|
+
def __html__(self):
|
|
427
|
+
return self.value
|
|
428
|
+
|
|
429
|
+
def __repr__(self):
|
|
430
|
+
return '<%s %r>' % (
|
|
431
|
+
self.__class__.__name__, self.value)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def html_quote(value, force=True):
|
|
435
|
+
if not force and hasattr(value, '__html__'):
|
|
436
|
+
return value.__html__()
|
|
437
|
+
if value is None:
|
|
438
|
+
return ''
|
|
439
|
+
if not isinstance(value, basestring_):
|
|
440
|
+
value = coerce_text(value)
|
|
441
|
+
if sys.version >= "3" and isinstance(value, bytes):
|
|
442
|
+
value = escape(value.decode('latin1'), 1)
|
|
443
|
+
value = value.encode('latin1')
|
|
444
|
+
else:
|
|
445
|
+
value = escape(value, 1)
|
|
446
|
+
if sys.version < "3":
|
|
447
|
+
if is_unicode(value):
|
|
448
|
+
value = value.encode('ascii', 'xmlcharrefreplace')
|
|
449
|
+
return value
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def url(v):
|
|
453
|
+
v = coerce_text(v)
|
|
454
|
+
if is_unicode(v):
|
|
455
|
+
v = v.encode('utf8')
|
|
456
|
+
return url_quote(v)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def attr(**kw):
|
|
460
|
+
kw = list(kw.iteritems())
|
|
461
|
+
kw.sort()
|
|
462
|
+
parts = []
|
|
463
|
+
for name, value in kw:
|
|
464
|
+
if value is None:
|
|
465
|
+
continue
|
|
466
|
+
if name.endswith('_'):
|
|
467
|
+
name = name[:-1]
|
|
468
|
+
parts.append('%s="%s"' % (html_quote(name), html_quote(value)))
|
|
469
|
+
return html(' '.join(parts))
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
class HTMLTemplate(Template):
|
|
473
|
+
|
|
474
|
+
default_namespace = Template.default_namespace.copy()
|
|
475
|
+
default_namespace.update(dict(
|
|
476
|
+
html=html,
|
|
477
|
+
attr=attr,
|
|
478
|
+
url=url,
|
|
479
|
+
html_quote=html_quote,
|
|
480
|
+
))
|
|
481
|
+
|
|
482
|
+
def _repr(self, value, pos):
|
|
483
|
+
if hasattr(value, '__html__'):
|
|
484
|
+
value = value.__html__()
|
|
485
|
+
quote = False
|
|
486
|
+
else:
|
|
487
|
+
quote = True
|
|
488
|
+
plain = Template._repr(self, value, pos)
|
|
489
|
+
if quote:
|
|
490
|
+
return html_quote(plain)
|
|
491
|
+
else:
|
|
492
|
+
return plain
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def sub_html(content, **kw):
|
|
496
|
+
name = kw.get('__name')
|
|
497
|
+
tmpl = HTMLTemplate(content, name=name)
|
|
498
|
+
return tmpl.substitute(kw)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
class TemplateDef(object):
|
|
502
|
+
def __init__(self, template, func_name, func_signature,
|
|
503
|
+
body, ns, pos, bound_self=None):
|
|
504
|
+
self._template = template
|
|
505
|
+
self._func_name = func_name
|
|
506
|
+
self._func_signature = func_signature
|
|
507
|
+
self._body = body
|
|
508
|
+
self._ns = ns
|
|
509
|
+
self._pos = pos
|
|
510
|
+
self._bound_self = bound_self
|
|
511
|
+
|
|
512
|
+
def __repr__(self):
|
|
513
|
+
return '<tempita function %s(%s) at %s:%s>' % (
|
|
514
|
+
self._func_name, self._func_signature,
|
|
515
|
+
self._template.name, self._pos)
|
|
516
|
+
|
|
517
|
+
def __str__(self):
|
|
518
|
+
return self()
|
|
519
|
+
|
|
520
|
+
def __call__(self, *args, **kw):
|
|
521
|
+
values = self._parse_signature(args, kw)
|
|
522
|
+
ns = self._ns.copy()
|
|
523
|
+
ns.update(values)
|
|
524
|
+
if self._bound_self is not None:
|
|
525
|
+
ns['self'] = self._bound_self
|
|
526
|
+
out = []
|
|
527
|
+
subdefs = {}
|
|
528
|
+
self._template._interpret_codes(self._body, ns, out, subdefs)
|
|
529
|
+
return ''.join(out)
|
|
530
|
+
|
|
531
|
+
def __get__(self, obj, type=None):
|
|
532
|
+
if obj is None:
|
|
533
|
+
return self
|
|
534
|
+
return self.__class__(
|
|
535
|
+
self._template, self._func_name, self._func_signature,
|
|
536
|
+
self._body, self._ns, self._pos, bound_self=obj)
|
|
537
|
+
|
|
538
|
+
def _parse_signature(self, args, kw):
|
|
539
|
+
values = {}
|
|
540
|
+
sig_args, var_args, var_kw, defaults = self._func_signature
|
|
541
|
+
extra_kw = {}
|
|
542
|
+
for name, value in iteritems(kw):
|
|
543
|
+
if not var_kw and name not in sig_args:
|
|
544
|
+
raise TypeError(
|
|
545
|
+
'Unexpected argument %s' % name)
|
|
546
|
+
if name in sig_args:
|
|
547
|
+
values[sig_args] = value
|
|
548
|
+
else:
|
|
549
|
+
extra_kw[name] = value
|
|
550
|
+
args = list(args)
|
|
551
|
+
sig_args = list(sig_args)
|
|
552
|
+
while args:
|
|
553
|
+
while sig_args and sig_args[0] in values:
|
|
554
|
+
sig_args.pop(0)
|
|
555
|
+
if sig_args:
|
|
556
|
+
name = sig_args.pop(0)
|
|
557
|
+
values[name] = args.pop(0)
|
|
558
|
+
elif var_args:
|
|
559
|
+
values[var_args] = tuple(args)
|
|
560
|
+
break
|
|
561
|
+
else:
|
|
562
|
+
raise TypeError(
|
|
563
|
+
'Extra position arguments: %s'
|
|
564
|
+
% ', '.join(repr(v) for v in args))
|
|
565
|
+
for name, value_expr in iteritems(defaults):
|
|
566
|
+
if name not in values:
|
|
567
|
+
values[name] = self._template._eval(
|
|
568
|
+
value_expr, self._ns, self._pos)
|
|
569
|
+
for name in sig_args:
|
|
570
|
+
if name not in values:
|
|
571
|
+
raise TypeError(
|
|
572
|
+
'Missing argument: %s' % name)
|
|
573
|
+
if var_kw:
|
|
574
|
+
values[var_kw] = extra_kw
|
|
575
|
+
return values
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class TemplateObject(object):
|
|
579
|
+
|
|
580
|
+
def __init__(self, name):
|
|
581
|
+
self.__name = name
|
|
582
|
+
self.get = TemplateObjectGetter(self)
|
|
583
|
+
|
|
584
|
+
def __repr__(self):
|
|
585
|
+
return '<%s %s>' % (self.__class__.__name__, self.__name)
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
class TemplateObjectGetter(object):
|
|
589
|
+
|
|
590
|
+
def __init__(self, template_obj):
|
|
591
|
+
self.__template_obj = template_obj
|
|
592
|
+
|
|
593
|
+
def __getattr__(self, attr):
|
|
594
|
+
return getattr(self.__template_obj, attr, Empty)
|
|
595
|
+
|
|
596
|
+
def __repr__(self):
|
|
597
|
+
return '<%s around %r>' % (self.__class__.__name__, self.__template_obj)
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
class _Empty(object):
|
|
601
|
+
def __call__(self, *args, **kw):
|
|
602
|
+
return self
|
|
603
|
+
|
|
604
|
+
def __str__(self):
|
|
605
|
+
return ''
|
|
606
|
+
|
|
607
|
+
def __repr__(self):
|
|
608
|
+
return 'Empty'
|
|
609
|
+
|
|
610
|
+
def __unicode__(self):
|
|
611
|
+
return u''
|
|
612
|
+
|
|
613
|
+
def __iter__(self):
|
|
614
|
+
return iter(())
|
|
615
|
+
|
|
616
|
+
def __bool__(self):
|
|
617
|
+
return False
|
|
618
|
+
|
|
619
|
+
if sys.version < "3":
|
|
620
|
+
__nonzero__ = __bool__
|
|
621
|
+
|
|
622
|
+
Empty = _Empty()
|
|
623
|
+
del _Empty
|
|
624
|
+
|
|
625
|
+
############################################################
|
|
626
|
+
## Lexing and Parsing
|
|
627
|
+
############################################################
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def lex(s, name=None, trim_whitespace=True, line_offset=0):
|
|
631
|
+
"""
|
|
632
|
+
Lex a string into chunks:
|
|
633
|
+
|
|
634
|
+
>>> lex('hey')
|
|
635
|
+
['hey']
|
|
636
|
+
>>> lex('hey {{you}}')
|
|
637
|
+
['hey ', ('you', (1, 7))]
|
|
638
|
+
>>> lex('hey {{') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
639
|
+
Traceback (most recent call last):
|
|
640
|
+
...
|
|
641
|
+
TemplateError: No }} to finish last expression at line 1 column 7
|
|
642
|
+
>>> lex('hey }}') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
643
|
+
Traceback (most recent call last):
|
|
644
|
+
...
|
|
645
|
+
TemplateError: }} outside expression at line 1 column 7
|
|
646
|
+
>>> lex('hey {{ {{') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
647
|
+
Traceback (most recent call last):
|
|
648
|
+
...
|
|
649
|
+
TemplateError: {{ inside expression at line 1 column 10
|
|
650
|
+
|
|
651
|
+
"""
|
|
652
|
+
in_expr = False
|
|
653
|
+
chunks = []
|
|
654
|
+
last = 0
|
|
655
|
+
last_pos = (1, 1)
|
|
656
|
+
for match in token_re.finditer(s):
|
|
657
|
+
expr = match.group(0)
|
|
658
|
+
pos = find_position(s, match.end(), line_offset)
|
|
659
|
+
if expr == '{{' and in_expr:
|
|
660
|
+
raise TemplateError('{{ inside expression', position=pos,
|
|
661
|
+
name=name)
|
|
662
|
+
elif expr == '}}' and not in_expr:
|
|
663
|
+
raise TemplateError('}} outside expression', position=pos,
|
|
664
|
+
name=name)
|
|
665
|
+
if expr == '{{':
|
|
666
|
+
part = s[last:match.start()]
|
|
667
|
+
if part:
|
|
668
|
+
chunks.append(part)
|
|
669
|
+
in_expr = True
|
|
670
|
+
else:
|
|
671
|
+
chunks.append((s[last:match.start()], last_pos))
|
|
672
|
+
in_expr = False
|
|
673
|
+
last = match.end()
|
|
674
|
+
last_pos = pos
|
|
675
|
+
if in_expr:
|
|
676
|
+
raise TemplateError('No }} to finish last expression',
|
|
677
|
+
name=name, position=last_pos)
|
|
678
|
+
part = s[last:]
|
|
679
|
+
if part:
|
|
680
|
+
chunks.append(part)
|
|
681
|
+
if trim_whitespace:
|
|
682
|
+
chunks = trim_lex(chunks)
|
|
683
|
+
return chunks
|
|
684
|
+
|
|
685
|
+
statement_re = re.compile(r'^(?:if |elif |for |def |inherit |default |py:)')
|
|
686
|
+
single_statements = ['else', 'endif', 'endfor', 'enddef', 'continue', 'break']
|
|
687
|
+
trail_whitespace_re = re.compile(r'\n\r?[\t ]*$')
|
|
688
|
+
lead_whitespace_re = re.compile(r'^[\t ]*\n')
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
def trim_lex(tokens):
|
|
692
|
+
r"""
|
|
693
|
+
Takes a lexed set of tokens, and removes whitespace when there is
|
|
694
|
+
a directive on a line by itself:
|
|
695
|
+
|
|
696
|
+
>>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False)
|
|
697
|
+
>>> tokens
|
|
698
|
+
[('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny']
|
|
699
|
+
>>> trim_lex(tokens)
|
|
700
|
+
[('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y']
|
|
701
|
+
"""
|
|
702
|
+
last_trim = None
|
|
703
|
+
for i in range(len(tokens)):
|
|
704
|
+
current = tokens[i]
|
|
705
|
+
if isinstance(tokens[i], basestring_):
|
|
706
|
+
# we don't trim this
|
|
707
|
+
continue
|
|
708
|
+
item = current[0]
|
|
709
|
+
if not statement_re.search(item) and item not in single_statements:
|
|
710
|
+
continue
|
|
711
|
+
if not i:
|
|
712
|
+
prev = ''
|
|
713
|
+
else:
|
|
714
|
+
prev = tokens[i - 1]
|
|
715
|
+
if i + 1 >= len(tokens):
|
|
716
|
+
next_chunk = ''
|
|
717
|
+
else:
|
|
718
|
+
next_chunk = tokens[i + 1]
|
|
719
|
+
if (not isinstance(next_chunk, basestring_)
|
|
720
|
+
or not isinstance(prev, basestring_)):
|
|
721
|
+
continue
|
|
722
|
+
prev_ok = not prev or trail_whitespace_re.search(prev)
|
|
723
|
+
if i == 1 and not prev.strip():
|
|
724
|
+
prev_ok = True
|
|
725
|
+
if last_trim is not None and last_trim + 2 == i and not prev.strip():
|
|
726
|
+
prev_ok = 'last'
|
|
727
|
+
if (prev_ok
|
|
728
|
+
and (not next_chunk or lead_whitespace_re.search(next_chunk)
|
|
729
|
+
or (i == len(tokens) - 2 and not next_chunk.strip()))):
|
|
730
|
+
if prev:
|
|
731
|
+
if ((i == 1 and not prev.strip())
|
|
732
|
+
or prev_ok == 'last'):
|
|
733
|
+
tokens[i - 1] = ''
|
|
734
|
+
else:
|
|
735
|
+
m = trail_whitespace_re.search(prev)
|
|
736
|
+
# +1 to leave the leading \n on:
|
|
737
|
+
prev = prev[:m.start() + 1]
|
|
738
|
+
tokens[i - 1] = prev
|
|
739
|
+
if next_chunk:
|
|
740
|
+
last_trim = i
|
|
741
|
+
if i == len(tokens) - 2 and not next_chunk.strip():
|
|
742
|
+
tokens[i + 1] = ''
|
|
743
|
+
else:
|
|
744
|
+
m = lead_whitespace_re.search(next_chunk)
|
|
745
|
+
next_chunk = next_chunk[m.end():]
|
|
746
|
+
tokens[i + 1] = next_chunk
|
|
747
|
+
return tokens
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def find_position(string, index, line_offset):
|
|
751
|
+
"""Given a string and index, return (line, column)"""
|
|
752
|
+
leading = string[:index].splitlines()
|
|
753
|
+
return (len(leading) + line_offset, len(leading[-1]) + 1)
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
def parse(s, name=None, line_offset=0):
|
|
757
|
+
r"""
|
|
758
|
+
Parses a string into a kind of AST
|
|
759
|
+
|
|
760
|
+
>>> parse('{{x}}')
|
|
761
|
+
[('expr', (1, 3), 'x')]
|
|
762
|
+
>>> parse('foo')
|
|
763
|
+
['foo']
|
|
764
|
+
>>> parse('{{if x}}test{{endif}}')
|
|
765
|
+
[('cond', (1, 3), ('if', (1, 3), 'x', ['test']))]
|
|
766
|
+
>>> parse('series->{{for x in y}}x={{x}}{{endfor}}')
|
|
767
|
+
['series->', ('for', (1, 11), ('x',), 'y', ['x=', ('expr', (1, 27), 'x')])]
|
|
768
|
+
>>> parse('{{for x, y in z:}}{{continue}}{{endfor}}')
|
|
769
|
+
[('for', (1, 3), ('x', 'y'), 'z', [('continue', (1, 21))])]
|
|
770
|
+
>>> parse('{{py:x=1}}')
|
|
771
|
+
[('py', (1, 3), 'x=1')]
|
|
772
|
+
>>> parse('{{if x}}a{{elif y}}b{{else}}c{{endif}}')
|
|
773
|
+
[('cond', (1, 3), ('if', (1, 3), 'x', ['a']), ('elif', (1, 12), 'y', ['b']), ('else', (1, 23), None, ['c']))]
|
|
774
|
+
|
|
775
|
+
Some exceptions::
|
|
776
|
+
|
|
777
|
+
>>> parse('{{continue}}') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
778
|
+
Traceback (most recent call last):
|
|
779
|
+
...
|
|
780
|
+
TemplateError: continue outside of for loop at line 1 column 3
|
|
781
|
+
>>> parse('{{if x}}foo') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
782
|
+
Traceback (most recent call last):
|
|
783
|
+
...
|
|
784
|
+
TemplateError: No {{endif}} at line 1 column 3
|
|
785
|
+
>>> parse('{{else}}') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
786
|
+
Traceback (most recent call last):
|
|
787
|
+
...
|
|
788
|
+
TemplateError: else outside of an if block at line 1 column 3
|
|
789
|
+
>>> parse('{{if x}}{{for x in y}}{{endif}}{{endfor}}') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
790
|
+
Traceback (most recent call last):
|
|
791
|
+
...
|
|
792
|
+
TemplateError: Unexpected endif at line 1 column 25
|
|
793
|
+
>>> parse('{{if}}{{endif}}') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
794
|
+
Traceback (most recent call last):
|
|
795
|
+
...
|
|
796
|
+
TemplateError: if with no expression at line 1 column 3
|
|
797
|
+
>>> parse('{{for x y}}{{endfor}}') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
798
|
+
Traceback (most recent call last):
|
|
799
|
+
...
|
|
800
|
+
TemplateError: Bad for (no "in") in 'x y' at line 1 column 3
|
|
801
|
+
>>> parse('{{py:x=1\ny=2}}') # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
802
|
+
Traceback (most recent call last):
|
|
803
|
+
...
|
|
804
|
+
TemplateError: Multi-line py blocks must start with a newline at line 1 column 3
|
|
805
|
+
"""
|
|
806
|
+
tokens = lex(s, name=name, line_offset=line_offset)
|
|
807
|
+
result = []
|
|
808
|
+
while tokens:
|
|
809
|
+
next_chunk, tokens = parse_expr(tokens, name)
|
|
810
|
+
result.append(next_chunk)
|
|
811
|
+
return result
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
def parse_expr(tokens, name, context=()):
|
|
815
|
+
if isinstance(tokens[0], basestring_):
|
|
816
|
+
return tokens[0], tokens[1:]
|
|
817
|
+
expr, pos = tokens[0]
|
|
818
|
+
expr = expr.strip()
|
|
819
|
+
if expr.startswith('py:'):
|
|
820
|
+
expr = expr[3:].lstrip(' \t')
|
|
821
|
+
if expr.startswith('\n') or expr.startswith('\r'):
|
|
822
|
+
expr = expr.lstrip('\r\n')
|
|
823
|
+
if '\r' in expr:
|
|
824
|
+
expr = expr.replace('\r\n', '\n')
|
|
825
|
+
expr = expr.replace('\r', '')
|
|
826
|
+
expr += '\n'
|
|
827
|
+
else:
|
|
828
|
+
if '\n' in expr:
|
|
829
|
+
raise TemplateError(
|
|
830
|
+
'Multi-line py blocks must start with a newline',
|
|
831
|
+
position=pos, name=name)
|
|
832
|
+
return ('py', pos, expr), tokens[1:]
|
|
833
|
+
elif expr in ('continue', 'break'):
|
|
834
|
+
if 'for' not in context:
|
|
835
|
+
raise TemplateError(
|
|
836
|
+
'continue outside of for loop',
|
|
837
|
+
position=pos, name=name)
|
|
838
|
+
return (expr, pos), tokens[1:]
|
|
839
|
+
elif expr.startswith('if '):
|
|
840
|
+
return parse_cond(tokens, name, context)
|
|
841
|
+
elif (expr.startswith('elif ')
|
|
842
|
+
or expr == 'else'):
|
|
843
|
+
raise TemplateError(
|
|
844
|
+
'%s outside of an if block' % expr.split()[0],
|
|
845
|
+
position=pos, name=name)
|
|
846
|
+
elif expr in ('if', 'elif', 'for'):
|
|
847
|
+
raise TemplateError(
|
|
848
|
+
'%s with no expression' % expr,
|
|
849
|
+
position=pos, name=name)
|
|
850
|
+
elif expr in ('endif', 'endfor', 'enddef'):
|
|
851
|
+
raise TemplateError(
|
|
852
|
+
'Unexpected %s' % expr,
|
|
853
|
+
position=pos, name=name)
|
|
854
|
+
elif expr.startswith('for '):
|
|
855
|
+
return parse_for(tokens, name, context)
|
|
856
|
+
elif expr.startswith('default '):
|
|
857
|
+
return parse_default(tokens, name, context)
|
|
858
|
+
elif expr.startswith('inherit '):
|
|
859
|
+
return parse_inherit(tokens, name, context)
|
|
860
|
+
elif expr.startswith('def '):
|
|
861
|
+
return parse_def(tokens, name, context)
|
|
862
|
+
elif expr.startswith('#'):
|
|
863
|
+
return ('comment', pos, tokens[0][0]), tokens[1:]
|
|
864
|
+
return ('expr', pos, tokens[0][0]), tokens[1:]
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def parse_cond(tokens, name, context):
|
|
868
|
+
start = tokens[0][1]
|
|
869
|
+
pieces = []
|
|
870
|
+
context = context + ('if',)
|
|
871
|
+
while 1:
|
|
872
|
+
if not tokens:
|
|
873
|
+
raise TemplateError(
|
|
874
|
+
'Missing {{endif}}',
|
|
875
|
+
position=start, name=name)
|
|
876
|
+
if (isinstance(tokens[0], tuple)
|
|
877
|
+
and tokens[0][0] == 'endif'):
|
|
878
|
+
return ('cond', start) + tuple(pieces), tokens[1:]
|
|
879
|
+
next_chunk, tokens = parse_one_cond(tokens, name, context)
|
|
880
|
+
pieces.append(next_chunk)
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
def parse_one_cond(tokens, name, context):
|
|
884
|
+
(first, pos), tokens = tokens[0], tokens[1:]
|
|
885
|
+
content = []
|
|
886
|
+
if first.endswith(':'):
|
|
887
|
+
first = first[:-1]
|
|
888
|
+
if first.startswith('if '):
|
|
889
|
+
part = ('if', pos, first[3:].lstrip(), content)
|
|
890
|
+
elif first.startswith('elif '):
|
|
891
|
+
part = ('elif', pos, first[5:].lstrip(), content)
|
|
892
|
+
elif first == 'else':
|
|
893
|
+
part = ('else', pos, None, content)
|
|
894
|
+
else:
|
|
895
|
+
assert 0, "Unexpected token %r at %s" % (first, pos)
|
|
896
|
+
while 1:
|
|
897
|
+
if not tokens:
|
|
898
|
+
raise TemplateError(
|
|
899
|
+
'No {{endif}}',
|
|
900
|
+
position=pos, name=name)
|
|
901
|
+
if (isinstance(tokens[0], tuple)
|
|
902
|
+
and (tokens[0][0] == 'endif'
|
|
903
|
+
or tokens[0][0].startswith('elif ')
|
|
904
|
+
or tokens[0][0] == 'else')):
|
|
905
|
+
return part, tokens
|
|
906
|
+
next_chunk, tokens = parse_expr(tokens, name, context)
|
|
907
|
+
content.append(next_chunk)
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
def parse_for(tokens, name, context):
|
|
911
|
+
first, pos = tokens[0]
|
|
912
|
+
tokens = tokens[1:]
|
|
913
|
+
context = ('for',) + context
|
|
914
|
+
content = []
|
|
915
|
+
assert first.startswith('for ')
|
|
916
|
+
if first.endswith(':'):
|
|
917
|
+
first = first[:-1]
|
|
918
|
+
first = first[3:].strip()
|
|
919
|
+
match = in_re.search(first)
|
|
920
|
+
if not match:
|
|
921
|
+
raise TemplateError(
|
|
922
|
+
'Bad for (no "in") in %r' % first,
|
|
923
|
+
position=pos, name=name)
|
|
924
|
+
vars = first[:match.start()]
|
|
925
|
+
if '(' in vars:
|
|
926
|
+
raise TemplateError(
|
|
927
|
+
'You cannot have () in the variable section of a for loop (%r)'
|
|
928
|
+
% vars, position=pos, name=name)
|
|
929
|
+
vars = tuple([
|
|
930
|
+
v.strip() for v in first[:match.start()].split(',')
|
|
931
|
+
if v.strip()])
|
|
932
|
+
expr = first[match.end():]
|
|
933
|
+
while 1:
|
|
934
|
+
if not tokens:
|
|
935
|
+
raise TemplateError(
|
|
936
|
+
'No {{endfor}}',
|
|
937
|
+
position=pos, name=name)
|
|
938
|
+
if (isinstance(tokens[0], tuple)
|
|
939
|
+
and tokens[0][0] == 'endfor'):
|
|
940
|
+
return ('for', pos, vars, expr, content), tokens[1:]
|
|
941
|
+
next_chunk, tokens = parse_expr(tokens, name, context)
|
|
942
|
+
content.append(next_chunk)
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
def parse_default(tokens, name, context):
|
|
946
|
+
first, pos = tokens[0]
|
|
947
|
+
assert first.startswith('default ')
|
|
948
|
+
first = first.split(None, 1)[1]
|
|
949
|
+
parts = first.split('=', 1)
|
|
950
|
+
if len(parts) == 1:
|
|
951
|
+
raise TemplateError(
|
|
952
|
+
"Expression must be {{default var=value}}; no = found in %r" % first,
|
|
953
|
+
position=pos, name=name)
|
|
954
|
+
var = parts[0].strip()
|
|
955
|
+
if ',' in var:
|
|
956
|
+
raise TemplateError(
|
|
957
|
+
"{{default x, y = ...}} is not supported",
|
|
958
|
+
position=pos, name=name)
|
|
959
|
+
if not var_re.search(var):
|
|
960
|
+
raise TemplateError(
|
|
961
|
+
"Not a valid variable name for {{default}}: %r"
|
|
962
|
+
% var, position=pos, name=name)
|
|
963
|
+
expr = parts[1].strip()
|
|
964
|
+
return ('default', pos, var, expr), tokens[1:]
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
def parse_inherit(tokens, name, context):
|
|
968
|
+
first, pos = tokens[0]
|
|
969
|
+
assert first.startswith('inherit ')
|
|
970
|
+
expr = first.split(None, 1)[1]
|
|
971
|
+
return ('inherit', pos, expr), tokens[1:]
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def parse_def(tokens, name, context):
|
|
975
|
+
first, start = tokens[0]
|
|
976
|
+
tokens = tokens[1:]
|
|
977
|
+
assert first.startswith('def ')
|
|
978
|
+
first = first.split(None, 1)[1]
|
|
979
|
+
if first.endswith(':'):
|
|
980
|
+
first = first[:-1]
|
|
981
|
+
if '(' not in first:
|
|
982
|
+
func_name = first
|
|
983
|
+
sig = ((), None, None, {})
|
|
984
|
+
elif not first.endswith(')'):
|
|
985
|
+
raise TemplateError("Function definition doesn't end with ): %s" % first,
|
|
986
|
+
position=start, name=name)
|
|
987
|
+
else:
|
|
988
|
+
first = first[:-1]
|
|
989
|
+
func_name, sig_text = first.split('(', 1)
|
|
990
|
+
sig = parse_signature(sig_text, name, start)
|
|
991
|
+
context = context + ('def',)
|
|
992
|
+
content = []
|
|
993
|
+
while 1:
|
|
994
|
+
if not tokens:
|
|
995
|
+
raise TemplateError(
|
|
996
|
+
'Missing {{enddef}}',
|
|
997
|
+
position=start, name=name)
|
|
998
|
+
if (isinstance(tokens[0], tuple)
|
|
999
|
+
and tokens[0][0] == 'enddef'):
|
|
1000
|
+
return ('def', start, func_name, sig, content), tokens[1:]
|
|
1001
|
+
next_chunk, tokens = parse_expr(tokens, name, context)
|
|
1002
|
+
content.append(next_chunk)
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
def parse_signature(sig_text, name, pos):
|
|
1006
|
+
if PY2 and isinstance(sig_text, str):
|
|
1007
|
+
lines = BytesIO(sig_text).readline
|
|
1008
|
+
else:
|
|
1009
|
+
lines = StringIO(sig_text).readline
|
|
1010
|
+
|
|
1011
|
+
tokens = tokenize.generate_tokens(lines)
|
|
1012
|
+
sig_args = []
|
|
1013
|
+
var_arg = None
|
|
1014
|
+
var_kw = None
|
|
1015
|
+
defaults = {}
|
|
1016
|
+
|
|
1017
|
+
def get_token(pos=False):
|
|
1018
|
+
try:
|
|
1019
|
+
tok_type, tok_string, (srow, scol), (erow, ecol), line = next(tokens)
|
|
1020
|
+
except StopIteration:
|
|
1021
|
+
return tokenize.ENDMARKER, ''
|
|
1022
|
+
if pos:
|
|
1023
|
+
return tok_type, tok_string, (srow, scol), (erow, ecol)
|
|
1024
|
+
else:
|
|
1025
|
+
return tok_type, tok_string
|
|
1026
|
+
while 1:
|
|
1027
|
+
var_arg_type = None
|
|
1028
|
+
tok_type, tok_string = get_token()
|
|
1029
|
+
if tok_type == tokenize.ENDMARKER:
|
|
1030
|
+
break
|
|
1031
|
+
if tok_type == tokenize.OP and (tok_string == '*' or tok_string == '**'):
|
|
1032
|
+
var_arg_type = tok_string
|
|
1033
|
+
tok_type, tok_string = get_token()
|
|
1034
|
+
if tok_type != tokenize.NAME:
|
|
1035
|
+
raise TemplateError('Invalid signature: (%s)' % sig_text,
|
|
1036
|
+
position=pos, name=name)
|
|
1037
|
+
var_name = tok_string
|
|
1038
|
+
tok_type, tok_string = get_token()
|
|
1039
|
+
if tok_type == tokenize.ENDMARKER or (tok_type == tokenize.OP and tok_string == ','):
|
|
1040
|
+
if var_arg_type == '*':
|
|
1041
|
+
var_arg = var_name
|
|
1042
|
+
elif var_arg_type == '**':
|
|
1043
|
+
var_kw = var_name
|
|
1044
|
+
else:
|
|
1045
|
+
sig_args.append(var_name)
|
|
1046
|
+
if tok_type == tokenize.ENDMARKER:
|
|
1047
|
+
break
|
|
1048
|
+
continue
|
|
1049
|
+
if var_arg_type is not None:
|
|
1050
|
+
raise TemplateError('Invalid signature: (%s)' % sig_text,
|
|
1051
|
+
position=pos, name=name)
|
|
1052
|
+
if tok_type == tokenize.OP and tok_string == '=':
|
|
1053
|
+
nest_type = None
|
|
1054
|
+
unnest_type = None
|
|
1055
|
+
nest_count = 0
|
|
1056
|
+
start_pos = end_pos = None
|
|
1057
|
+
parts = []
|
|
1058
|
+
while 1:
|
|
1059
|
+
tok_type, tok_string, s, e = get_token(True)
|
|
1060
|
+
if start_pos is None:
|
|
1061
|
+
start_pos = s
|
|
1062
|
+
end_pos = e
|
|
1063
|
+
if tok_type == tokenize.ENDMARKER and nest_count:
|
|
1064
|
+
raise TemplateError('Invalid signature: (%s)' % sig_text,
|
|
1065
|
+
position=pos, name=name)
|
|
1066
|
+
if (not nest_count and
|
|
1067
|
+
(tok_type == tokenize.ENDMARKER or (tok_type == tokenize.OP and tok_string == ','))):
|
|
1068
|
+
default_expr = isolate_expression(sig_text, start_pos, end_pos)
|
|
1069
|
+
defaults[var_name] = default_expr
|
|
1070
|
+
sig_args.append(var_name)
|
|
1071
|
+
break
|
|
1072
|
+
parts.append((tok_type, tok_string))
|
|
1073
|
+
if nest_count and tok_type == tokenize.OP and tok_string == nest_type:
|
|
1074
|
+
nest_count += 1
|
|
1075
|
+
elif nest_count and tok_type == tokenize.OP and tok_string == unnest_type:
|
|
1076
|
+
nest_count -= 1
|
|
1077
|
+
if not nest_count:
|
|
1078
|
+
nest_type = unnest_type = None
|
|
1079
|
+
elif not nest_count and tok_type == tokenize.OP and tok_string in ('(', '[', '{'):
|
|
1080
|
+
nest_type = tok_string
|
|
1081
|
+
nest_count = 1
|
|
1082
|
+
unnest_type = {'(': ')', '[': ']', '{': '}'}[nest_type]
|
|
1083
|
+
return sig_args, var_arg, var_kw, defaults
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
def isolate_expression(string, start_pos, end_pos):
|
|
1087
|
+
srow, scol = start_pos
|
|
1088
|
+
srow -= 1
|
|
1089
|
+
erow, ecol = end_pos
|
|
1090
|
+
erow -= 1
|
|
1091
|
+
lines = string.splitlines(True)
|
|
1092
|
+
if srow == erow:
|
|
1093
|
+
return lines[srow][scol:ecol]
|
|
1094
|
+
parts = [lines[srow][scol:]]
|
|
1095
|
+
parts.extend(lines[srow+1:erow])
|
|
1096
|
+
if erow < len(lines):
|
|
1097
|
+
# It'll sometimes give (end_row_past_finish, 0)
|
|
1098
|
+
parts.append(lines[erow][:ecol])
|
|
1099
|
+
return ''.join(parts)
|
|
1100
|
+
|
|
1101
|
+
_fill_command_usage = """\
|
|
1102
|
+
%prog [OPTIONS] TEMPLATE arg=value
|
|
1103
|
+
|
|
1104
|
+
Use py:arg=value to set a Python value; otherwise all values are
|
|
1105
|
+
strings.
|
|
1106
|
+
"""
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
def fill_command(args=None):
|
|
1110
|
+
import sys
|
|
1111
|
+
import optparse
|
|
1112
|
+
import pkg_resources
|
|
1113
|
+
import os
|
|
1114
|
+
if args is None:
|
|
1115
|
+
args = sys.argv[1:]
|
|
1116
|
+
dist = pkg_resources.get_distribution('Paste')
|
|
1117
|
+
parser = optparse.OptionParser(
|
|
1118
|
+
version=coerce_text(dist),
|
|
1119
|
+
usage=_fill_command_usage)
|
|
1120
|
+
parser.add_option(
|
|
1121
|
+
'-o', '--output',
|
|
1122
|
+
dest='output',
|
|
1123
|
+
metavar="FILENAME",
|
|
1124
|
+
help="File to write output to (default stdout)")
|
|
1125
|
+
parser.add_option(
|
|
1126
|
+
'--html',
|
|
1127
|
+
dest='use_html',
|
|
1128
|
+
action='store_true',
|
|
1129
|
+
help="Use HTML style filling (including automatic HTML quoting)")
|
|
1130
|
+
parser.add_option(
|
|
1131
|
+
'--env',
|
|
1132
|
+
dest='use_env',
|
|
1133
|
+
action='store_true',
|
|
1134
|
+
help="Put the environment in as top-level variables")
|
|
1135
|
+
options, args = parser.parse_args(args)
|
|
1136
|
+
if len(args) < 1:
|
|
1137
|
+
print('You must give a template filename')
|
|
1138
|
+
sys.exit(2)
|
|
1139
|
+
template_name = args[0]
|
|
1140
|
+
args = args[1:]
|
|
1141
|
+
vars = {}
|
|
1142
|
+
if options.use_env:
|
|
1143
|
+
vars.update(os.environ)
|
|
1144
|
+
for value in args:
|
|
1145
|
+
if '=' not in value:
|
|
1146
|
+
print(('Bad argument: %r' % value))
|
|
1147
|
+
sys.exit(2)
|
|
1148
|
+
name, value = value.split('=', 1)
|
|
1149
|
+
if name.startswith('py:'):
|
|
1150
|
+
name = name[:3]
|
|
1151
|
+
value = eval(value)
|
|
1152
|
+
vars[name] = value
|
|
1153
|
+
if template_name == '-':
|
|
1154
|
+
template_content = sys.stdin.read()
|
|
1155
|
+
template_name = '<stdin>'
|
|
1156
|
+
else:
|
|
1157
|
+
f = open(template_name, 'rb')
|
|
1158
|
+
template_content = f.read()
|
|
1159
|
+
f.close()
|
|
1160
|
+
if options.use_html:
|
|
1161
|
+
TemplateClass = HTMLTemplate
|
|
1162
|
+
else:
|
|
1163
|
+
TemplateClass = Template
|
|
1164
|
+
template = TemplateClass(template_content, name=template_name)
|
|
1165
|
+
result = template.substitute(vars)
|
|
1166
|
+
if options.output:
|
|
1167
|
+
f = open(options.output, 'wb')
|
|
1168
|
+
f.write(result)
|
|
1169
|
+
f.close()
|
|
1170
|
+
else:
|
|
1171
|
+
sys.stdout.write(result)
|
|
1172
|
+
|
|
1173
|
+
if __name__ == '__main__':
|
|
1174
|
+
fill_command()
|