new package pyscript, derived from zope.app.PythonPage
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1867 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
52cf72da7f
commit
dc7aa556c5
9 changed files with 500 additions and 0 deletions
29
pyscript/README.txt
Normal file
29
pyscript/README.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
Python Page
|
||||
===========
|
||||
|
||||
Python Page provides the user with a content object that interprets
|
||||
Python in content space. To save typing and useless messing with
|
||||
output, any free-standing string and print statement are considered
|
||||
for output; see the example below.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
Create a new content type called "Python Page" and enter the following
|
||||
code example::
|
||||
|
||||
'''
|
||||
<html>
|
||||
<body>
|
||||
<ul>
|
||||
'''
|
||||
|
||||
import time
|
||||
print time.asctime()
|
||||
|
||||
'''
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
3
pyscript/__init__.py
Normal file
3
pyscript/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
$Id$
|
||||
"""
|
47
pyscript/browser.py
Normal file
47
pyscript/browser.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Corporation 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Python Page Browser Views
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.app.form.browser.editview import EditView
|
||||
from zope.app.i18n import ZopeMessageFactory as _
|
||||
|
||||
|
||||
class PythonPageEval(object):
|
||||
"""Evaluate the Python Page."""
|
||||
|
||||
def index(self, **kw):
|
||||
"""Call a Python Page"""
|
||||
self.request.response.setHeader('content-type',
|
||||
self.context.contentType)
|
||||
return str(self.context(self.request, **kw))
|
||||
|
||||
|
||||
class PythonPageEditView(EditView):
|
||||
"""Edit View Class for Python Page."""
|
||||
|
||||
syntaxError = None
|
||||
|
||||
def update(self):
|
||||
"""Update the content with the HTML form data."""
|
||||
try:
|
||||
status = super(PythonPageEditView, self).update()
|
||||
except SyntaxError, err:
|
||||
self.syntaxError = err
|
||||
status = _('A syntax error occurred.')
|
||||
self.update_status = status
|
||||
|
||||
return status
|
81
pyscript/configure.zcml
Normal file
81
pyscript/configure.zcml
Normal file
|
@ -0,0 +1,81 @@
|
|||
<configure
|
||||
xmlns="http://namespaces.zope.org/zope"
|
||||
xmlns:browser="http://namespaces.zope.org/browser"
|
||||
i18n_domain="zope">
|
||||
|
||||
<interface
|
||||
interface=".interfaces.IPythonPage"
|
||||
type="zope.app.content.interfaces.IContentType"
|
||||
/>
|
||||
|
||||
<class class=".script.PythonPage">
|
||||
<factory
|
||||
id="cybertools.pyscript.PythonPage"
|
||||
title="Python Page"
|
||||
description="A simple, content-based Python Page"
|
||||
/>
|
||||
<require
|
||||
permission="zope.View"
|
||||
interface=".interfaces.IPythonPage"
|
||||
/>
|
||||
<require
|
||||
permission="zope.ManageContent"
|
||||
set_attributes="source contentType"
|
||||
/>
|
||||
<implements
|
||||
interface="zope.annotation.interfaces.IAttributeAnnotatable"
|
||||
/>
|
||||
</class>
|
||||
|
||||
|
||||
<!-- browser directives -->
|
||||
|
||||
<browser:page
|
||||
name="index.html"
|
||||
for=".interfaces.IPythonPage"
|
||||
class=".browser.PythonPageEval"
|
||||
attribute="index"
|
||||
permission="zope.View"
|
||||
/>
|
||||
|
||||
<browser:addform
|
||||
label="Add Python Page"
|
||||
name="AddPythonPage.html"
|
||||
schema=".interfaces.IPythonPage"
|
||||
content_factory=".script.PythonPage"
|
||||
permission="zope.ManageContent"
|
||||
/>
|
||||
|
||||
<browser:addMenuItem
|
||||
class=".script.PythonPage"
|
||||
title="Python Page"
|
||||
description="An Python Page"
|
||||
permission="zope.ManageContent"
|
||||
view="AddPythonPage.html"
|
||||
/>
|
||||
|
||||
<browser:editform
|
||||
for=".interfaces.IPythonPage"
|
||||
schema=".interfaces.IPythonPage"
|
||||
name="edit.html"
|
||||
label="Edit Python Page"
|
||||
class=".browser.PythonPageEditView"
|
||||
template="edit.pt"
|
||||
permission="zope.ManageContent"
|
||||
menu="zmi_views" title="Edit"
|
||||
/>
|
||||
|
||||
|
||||
<!-- Preview view - requires zope.app.preview -->
|
||||
|
||||
<configure package="zope.app.preview">
|
||||
<browser:page
|
||||
for="cybertools.pyscript.interfaces.IPythonPage"
|
||||
name="preview.html"
|
||||
template="preview.pt"
|
||||
permission="zope.ManageContent"
|
||||
menu="zmi_views" title="Preview"
|
||||
/>
|
||||
</configure>
|
||||
|
||||
</configure>
|
1
pyscript/cybertools.pyscript-configure.zcml
Normal file
1
pyscript/cybertools.pyscript-configure.zcml
Normal file
|
@ -0,0 +1 @@
|
|||
<include package="cybertools.pyscript" />
|
88
pyscript/edit.pt
Normal file
88
pyscript/edit.pt
Normal file
|
@ -0,0 +1,88 @@
|
|||
<tal:tag condition="view/update"/>
|
||||
<html metal:use-macro="context/@@standard_macros/view"
|
||||
i18n:domain="zope">
|
||||
<body>
|
||||
<div metal:fill-slot="body">
|
||||
|
||||
<div metal:define-macro="body">
|
||||
|
||||
<form action="." tal:attributes="action request/URL" method="post"
|
||||
enctype="multipart/form-data">
|
||||
|
||||
<div metal:define-macro="formbody">
|
||||
|
||||
<h3 tal:condition="view/label"
|
||||
tal:content="view/label"
|
||||
metal:define-slot="heading"
|
||||
i18n:translate="">Edit something</h3>
|
||||
|
||||
<p tal:define="status view/update"
|
||||
tal:condition="status"
|
||||
tal:content="status" i18n:translate=""/>
|
||||
|
||||
<p tal:condition="view/errors" i18n:translate="">
|
||||
There are <strong tal:content="python:len(view.errors)"
|
||||
i18n:name="num_errors">6</strong> input errors.
|
||||
</p>
|
||||
|
||||
<div class="message"
|
||||
tal:condition="view/syntaxError"
|
||||
tal:define="err view/syntaxError">
|
||||
<h3 i18n:translate="">Syntax Error:
|
||||
<span tal:content="err/msg" i18n:name="msg">invalid syntax</span>
|
||||
</h3>
|
||||
<div i18n:translate="">
|
||||
File
|
||||
"<span tal:replace="err/filename" i18n:name="filename">
|
||||
filename
|
||||
</span>",
|
||||
line <span tal:replace="err/lineno" i18n:name="lineno">10</span>,
|
||||
offset <span tal:replace="err/offset" i18n:name="offset">1</span>
|
||||
</div>
|
||||
<pre tal:content="python:err.text+'\n'+' '*err.offset+'^'">
|
||||
prin "foo"
|
||||
^
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div metal:define-slot="extra_info" tal:replace="nothing">
|
||||
</div>
|
||||
|
||||
<div class="row"
|
||||
metal:define-slot="extra_top" tal:replace="nothing">
|
||||
<div class="label">Extra top</div>
|
||||
<div class="field"><input type="text" style="width:100%" /></div>
|
||||
</div>
|
||||
|
||||
<div metal:use-macro="context/@@form_macros/widget_rows" />
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="row"
|
||||
metal:define-slot="extra_bottom" tal:replace="nothing">
|
||||
<div class="label">Extra bottom</div>
|
||||
<div class="field"><input type="text" style="width:100%" /></div>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="controls">
|
||||
<input type="submit" value="Refresh"
|
||||
i18n:attributes="value refresh-button" />
|
||||
<input type="submit" name="UPDATE_SUBMIT" value="Change"
|
||||
i18n:attributes="value submit-button"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" metal:define-slot="extra_buttons" tal:replace="nothing">
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
57
pyscript/interfaces.py
Normal file
57
pyscript/interfaces.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
#
|
||||
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
"""
|
||||
interface definitions for the pyscript package.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.interface import Interface
|
||||
from zope import schema
|
||||
|
||||
|
||||
class IPythonPage(Interface):
|
||||
"""Python Page
|
||||
|
||||
The Python Page acts as a simple content type that allows you to execute
|
||||
Python in content space. Additionally, if you have a free-standing
|
||||
triple-quoted string, it gets converted to a print statement
|
||||
automatically.
|
||||
"""
|
||||
|
||||
source = schema.SourceText(
|
||||
title=_(u"Source"),
|
||||
description=_(u"The source of the Python page."),
|
||||
required=True,
|
||||
)
|
||||
|
||||
contentType = schema.TextLine(
|
||||
title=_(u"Content Type"),
|
||||
description=_(u"The content type the script outputs."),
|
||||
required=True,
|
||||
default=u"text/html",
|
||||
)
|
||||
|
||||
def __call__(request, **kw):
|
||||
"""Execute the script.
|
||||
|
||||
The script will insert the `request` and all `**kw` as global
|
||||
variables. Furthermore, the variables `script` and `context` (which is
|
||||
the container of the script) will be added.
|
||||
"""
|
136
pyscript/script.py
Normal file
136
pyscript/script.py
Normal file
|
@ -0,0 +1,136 @@
|
|||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Corporation 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Python Page
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
import new
|
||||
import re
|
||||
from cStringIO import StringIO
|
||||
from persistent import Persistent
|
||||
from zope.proxy import removeAllProxies
|
||||
from zope.security.untrustedpython.builtins import SafeBuiltins
|
||||
from zope.security.untrustedpython.rcompile import compile
|
||||
from zope.traversing.api import getParent, getPath
|
||||
from zope.app.container.contained import Contained
|
||||
#from zope.app.interpreter.interfaces import IInterpreter
|
||||
from zope.interface import implements
|
||||
from zope.app.i18n import ZopeMessageFactory as _
|
||||
|
||||
from cybertools.pyscript.interfaces import IPythonPage
|
||||
|
||||
|
||||
class PythonPage(Contained, Persistent):
|
||||
"""Persistent Python Page - Content Type
|
||||
"""
|
||||
|
||||
implements(IPythonPage)
|
||||
|
||||
_v_compiled = None
|
||||
|
||||
def __init__(self, source=u'', contentType=u'text/plain'):
|
||||
"""Initialize the object."""
|
||||
super(PythonPage, self).__init__()
|
||||
self.source = source
|
||||
self.contentType = contentType
|
||||
|
||||
def __filename(self):
|
||||
if self.__parent__ is None:
|
||||
filename = 'N/A'
|
||||
else:
|
||||
filename = getPath(self)
|
||||
return filename
|
||||
|
||||
def setSource(self, source):
|
||||
"""Set the source of the page and compile it.
|
||||
|
||||
This method can raise a syntax error, if the source is not valid.
|
||||
"""
|
||||
self.__source = source
|
||||
self.__prepared_source = self.prepareSource(source)
|
||||
# Compile objects cannot be pickled
|
||||
self._v_compiled = Function(self.__prepared_source, self.__filename())
|
||||
|
||||
_tripleQuotedString = re.compile(
|
||||
r"^([ \t]*)[uU]?([rR]?)(('''|\"\"\")(.*)\4)", re.MULTILINE | re.DOTALL)
|
||||
|
||||
def prepareSource(self, source):
|
||||
"""Prepare source."""
|
||||
# compile() don't accept '\r' altogether
|
||||
source = source.replace("\r\n", "\n")
|
||||
source = source.replace("\r", "\n")
|
||||
if isinstance(source, unicode):
|
||||
# Use special conversion function to work around
|
||||
# compiler-module failure to handle unicode in literals
|
||||
try:
|
||||
source = source.encode('ascii')
|
||||
except UnicodeEncodeError:
|
||||
return self._tripleQuotedString.sub(_print_usrc, source)
|
||||
return self._tripleQuotedString.sub(r"\1print u\2\3", source)
|
||||
|
||||
|
||||
def getSource(self):
|
||||
"""Get the original source code."""
|
||||
return self.__source
|
||||
|
||||
source = property(getSource, setSource)
|
||||
|
||||
def __call__(self, request, **kw):
|
||||
output = StringIO()
|
||||
if self._v_compiled is None:
|
||||
self._v_compiled = Function(self.__prepared_source,
|
||||
self.__filename())
|
||||
kw['request'] = request
|
||||
kw['script'] = self
|
||||
kw['untrusted_output'] = output
|
||||
kw['printed'] = output
|
||||
kw['context'] = getParent(self)
|
||||
kw['script_result'] = None
|
||||
self._v_compiled(kw)
|
||||
result = kw['script_result']
|
||||
if result == output:
|
||||
result = result.getvalue()
|
||||
return result
|
||||
|
||||
|
||||
class Function(object):
|
||||
"""A compiled function.
|
||||
"""
|
||||
|
||||
def __init__(self, source, filename='<string>'):
|
||||
lines = []
|
||||
lines.insert(0, 'def dummy():')
|
||||
for line in source.splitlines():
|
||||
lines.append(' ' + line)
|
||||
lines.append('script_result = dummy()')
|
||||
source = '\n'.join(lines)
|
||||
print source
|
||||
self.code = compile(source, filename, 'exec')
|
||||
|
||||
def __call__(self, globals):
|
||||
globals['__builtins__'] = SafeBuiltins
|
||||
#fct = new.function(self.code, globals)
|
||||
exec self.code in globals, None
|
||||
#return fct()
|
||||
|
||||
|
||||
def _print_usrc(match):
|
||||
string = match.group(3)
|
||||
raw = match.group(2)
|
||||
if raw:
|
||||
return match.group(1)+'print '+`string`
|
||||
return match.group(1)+'print u'+match.group(3).encode('unicode-escape')
|
58
pyscript/tests.py
Normal file
58
pyscript/tests.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Corporation 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Tests for Python Page
|
||||
|
||||
$Id$
|
||||
"""
|
||||
import unittest
|
||||
|
||||
import zope.component
|
||||
from zope.interface import implements
|
||||
from zope.testing.doctestunit import DocTestSuite
|
||||
from zope.traversing.interfaces import IContainmentRoot
|
||||
from zope.traversing.interfaces import IPhysicallyLocatable
|
||||
from zope.traversing.adapters import RootPhysicallyLocatable
|
||||
from zope.location.traversing import LocationPhysicallyLocatable
|
||||
|
||||
from zope.app.container.contained import Contained
|
||||
#from zope.app.interpreter.interfaces import IInterpreter
|
||||
#from zope.app.interpreter.python import PythonInterpreter
|
||||
from zope.app.testing import placelesssetup, ztapi
|
||||
|
||||
class Root(Contained):
|
||||
implements(IContainmentRoot)
|
||||
|
||||
__parent__ = None
|
||||
__name__ = 'root'
|
||||
|
||||
def setUp(test):
|
||||
placelesssetup.setUp()
|
||||
sm = zope.component.getGlobalSiteManager()
|
||||
#sm.registerUtility(PythonInterpreter, IInterpreter, 'text/server-python')
|
||||
|
||||
ztapi.provideAdapter(None, IPhysicallyLocatable,
|
||||
LocationPhysicallyLocatable)
|
||||
ztapi.provideAdapter(IContainmentRoot, IPhysicallyLocatable,
|
||||
RootPhysicallyLocatable)
|
||||
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
DocTestSuite('cybertools.pyscript',
|
||||
setUp=setUp, tearDown=placelesssetup.tearDown),
|
||||
))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Add table
Reference in a new issue