1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Filesystem Page Template module
Zope object encapsulating a Page Template from the filesystem.
"""
__all__ = ("PageTemplateFile",)
import os
import sys
import re
import logging
from zope.pagetemplate.pagetemplate import PageTemplate
DEFAULT_ENCODING = "utf-8"
meta_pattern = re.compile(
br'\s*<meta\s+http-equiv=["\']?Content-Type["\']?'
br'\s+content=["\']?([^;]+);\s*charset=([^"\']+)["\']?\s*/?\s*>\s*',
re.IGNORECASE)
def package_home(gdict):
filename = gdict["__file__"]
return os.path.dirname(filename)
class PageTemplateFile(PageTemplate):
"Zope wrapper for filesystem Page Template using TAL, TALES, and METAL"
_v_last_read = 0
#_error_start = b'<!-- Page Template Diagnostics'
#_error_end = b'-->'
#_newline = b'\n'
def __init__(self, filename, _prefix=None):
path = self.get_path_from_prefix(_prefix)
self.filename = os.path.join(path, filename)
if not os.path.isfile(self.filename):
raise ValueError("No such file", self.filename)
def get_path_from_prefix(self, _prefix):
if isinstance(_prefix, str):
path = _prefix
else:
if _prefix is None:
_prefix = sys._getframe(2).f_globals
path = package_home(_prefix)
return path
def _prepare_html(self, text):
match = meta_pattern.search(text)
if match is not None:
type_, encoding = (x.decode('utf-8') for x in match.groups())
# TODO: Shouldn't <meta>/<?xml?> stripping
# be in PageTemplate.__call__()?
text = meta_pattern.sub(b"", text)
else:
type_ = None
encoding = DEFAULT_ENCODING
text = text.decode(encoding)
return text, type_
def _read_file(self):
__traceback_info__ = self.filename
f = open(self.filename, "rb")
try:
text = f.read(XML_PREFIX_MAX_LENGTH)
except:
f.close()
raise
type_ = sniff_type(text)
text += f.read()
if type_ != "text/xml":
text, type_ = self._prepare_html(text)
f.close()
return text, type_
def _cook_check(self):
if self._v_last_read and not __debug__:
return
__traceback_info__ = self.filename
try:
mtime = os.path.getmtime(self.filename)
except OSError:
mtime = 0
if self._v_program is not None and mtime == self._v_last_read:
return
text, type_ = self._read_file()
self.pt_edit(text, type_)
assert self._v_cooked
if self._v_errors:
logging.error('PageTemplateFile: Error in template %s: %s',
self.filename, '\n'.join(self._v_errors))
return
self._v_last_read = mtime
def pt_source_file(self):
return self.filename
def __getstate__(self):
raise TypeError("non-picklable object")
XML_PREFIXES = [
b"<?xml", # ascii, utf-8
b"\xef\xbb\xbf<?xml", # utf-8 w/ byte order mark
b"\0<\0?\0x\0m\0l", # utf-16 big endian
b"<\0?\0x\0m\0l\0", # utf-16 little endian
b"\xfe\xff\0<\0?\0x\0m\0l", # utf-16 big endian w/ byte order mark
b"\xff\xfe<\0?\0x\0m\0l\0", # utf-16 little endian w/ byte order mark
]
XML_PREFIX_MAX_LENGTH = max(map(len, XML_PREFIXES))
def sniff_type(text):
"""Return 'text/xml' if text appears to be XML, otherwise return None."""
for prefix in XML_PREFIXES:
if text.startswith(prefix):
return "text/xml"
return None
|