Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
Zope
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
Zope
Commits
9d17e061
Commit
9d17e061
authored
Aug 06, 2015
by
Gerhard Weis
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
https://github.com/zopefoundation/Zope
parents
5e26679d
ce2086b5
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
418 additions
and
61 deletions
+418
-61
.gitignore
.gitignore
+12
-13
CHANGES.rst
CHANGES.rst
+5
-0
docs/INSTALL-buildout.rst
docs/INSTALL-buildout.rst
+0
-0
docs/INSTALL-virtualenv.rst
docs/INSTALL-virtualenv.rst
+73
-0
docs/Makefile
docs/Makefile
+21
-21
docs/WSGI.rst
docs/WSGI.rst
+205
-0
docs/index.rst
docs/index.rst
+13
-10
docs/operation.rst
docs/operation.rst
+3
-2
src/ZPublisher/BaseRequest.py
src/ZPublisher/BaseRequest.py
+4
-0
src/ZPublisher/HTTPRequest.py
src/ZPublisher/HTTPRequest.py
+1
-0
src/ZPublisher/tests/testBaseRequest.py
src/ZPublisher/tests/testBaseRequest.py
+32
-2
src/ZPublisher/tests/testHTTPRequest.py
src/ZPublisher/tests/testHTTPRequest.py
+49
-13
No files found.
.gitignore
View file @
9d17e061
*.egg-info
*.py?
/.installed.cfg
/.mr.developer.cfg
/bin
/build
/develop
/develop-eggs
/docs/_build
/include
/lib
/dist
/eggs
/parts
docs/.build/
.installed.cfg
.mr.developer.cfg
bin/
build/
develop/
develop-eggs/
dist/
eggs/
include/
lib/
parts/
docs/_build/
CHANGES.rst
View file @
9d17e061
...
...
@@ -11,6 +11,11 @@ http://docs.zope.org/zope2/
Bugs Fixed
++++++++++
- LP #789863: Ensure that Request objects cannot be published / traversed
directly via a URL.
- Document running Zope as a WSGI application.
- Queue additional warning filters at the beginning of the queue in order to
allow overrides.
...
...
docs/INSTALL.rst
→
docs/INSTALL
-buildout
.rst
View file @
9d17e061
File moved
docs/INSTALL-virtualenv.rst
0 → 100644
View file @
9d17e061
Installing Zope with ``virtualenv``
===================================
.. highlight:: bash
This document describes how to install Zope into a ``virtualenv``.
Create a Virtual Environment
----------------------------
.. code-block:: sh
$ /opt/Python-2.7.9/bin/virtualenv z213
New python executable in z213/bin/python
Installing setuptools, pip, wheel...done.
$ cd z213
Install the Zope2 2.13.22 Software Packages
-------------------------------------------
.. code-block:: sh
$ bin/pip install \
--trusted-host download.zope.org \
--index http://download.zope.org/Zope2/index/2.13.22/ Zope2
Collecting Zope2
...
Successfully installed ...
Creating a Zope instance
------------------------
Once you've installed Zope, you will need to create an "instance
home". This is a directory that contains configuration and data for a
Zope server process. The instance home is created using the
``mkzopeinstance`` script:
.. code-block:: sh
$ bin/mkzopeinstance
You can specify the Python interpreter to use for the instance
explicitly:
.. code-block:: sh
$ bin/mkzopeinstance --python=bin/python
You will be asked to provide a user name and password for an
administrator's account during ``mkzopeinstance``. To see the available
command-line options, run the script with the ``--help`` option:
.. code-block:: sh
$ bin/mkzopeinstance --help
Using the ``virtualenv`` as the Zope Instance
---------------------------------------------
You can choose to use the ``virtualenv`` as your Zope instance:
.. code-block:: sh
$ bin/mkzopeinstance -d .
In this case, the instance files will be located in the
subdirectories of the ``virtualenv``:
- ``etc/`` will hold the configuration files.
- ``log/`` will hold the log files.
- ``var/`` will hold the database files.
docs/Makefile
View file @
9d17e061
...
...
@@ -9,7 +9,7 @@ PAPER =
# Internal variables.
PAPEROPT_a4
=
-D
latex_paper_size
=
a4
PAPEROPT_letter
=
-D
latex_paper_size
=
letter
ALLSPHINXOPTS
=
-d
.
build/doctrees
$
(
PAPEROPT_
$(PAPER)
)
$(SPHINXOPTS)
.
ALLSPHINXOPTS
=
-d
_
build/doctrees
$
(
PAPEROPT_
$(PAPER)
)
$(SPHINXOPTS)
.
.PHONY
:
help clean html web pickle htmlhelp latex changes linkcheck
...
...
@@ -24,52 +24,52 @@ help:
@
echo
" linkcheck to check all external links for integrity"
clean
:
-
rm
-rf
.
build/
*
-
rm
-rf
_
build/
*
html
:
mkdir
-p
.build/html .
build/doctrees
$(SPHINXBUILD)
-b
html
$(ALLSPHINXOPTS)
.
build/html
mkdir
-p
_build/html _
build/doctrees
$(SPHINXBUILD)
-b
html
$(ALLSPHINXOPTS)
_
build/html
@
echo
@
echo
"Build finished. The HTML pages are in
.
build/html."
@
echo
"Build finished. The HTML pages are in
_
build/html."
pickle
:
mkdir
-p
.build/pickle .
build/doctrees
$(SPHINXBUILD)
-b
pickle
$(ALLSPHINXOPTS)
.
build/pickle
mkdir
-p
_build/pickle _
build/doctrees
$(SPHINXBUILD)
-b
pickle
$(ALLSPHINXOPTS)
_
build/pickle
@
echo
@
echo
"Build finished; now you can process the pickle files."
web
:
pickle
json
:
mkdir
-p
.build/json .
build/doctrees
$(SPHINXBUILD)
-b
json
$(ALLSPHINXOPTS)
.
build/json
mkdir
-p
_build/json _
build/doctrees
$(SPHINXBUILD)
-b
json
$(ALLSPHINXOPTS)
_
build/json
@
echo
@
echo
"Build finished; now you can process the JSON files."
htmlhelp
:
mkdir
-p
.build/htmlhelp .
build/doctrees
$(SPHINXBUILD)
-b
htmlhelp
$(ALLSPHINXOPTS)
.
build/htmlhelp
mkdir
-p
_build/htmlhelp _
build/doctrees
$(SPHINXBUILD)
-b
htmlhelp
$(ALLSPHINXOPTS)
_
build/htmlhelp
@
echo
@
echo
"Build finished; now you can run HTML Help Workshop with the"
\
".hhp project file in
.
build/htmlhelp."
".hhp project file in
_
build/htmlhelp."
latex
:
mkdir
-p
.build/latex .
build/doctrees
$(SPHINXBUILD)
-b
latex
$(ALLSPHINXOPTS)
.
build/latex
mkdir
-p
_build/latex _
build/doctrees
$(SPHINXBUILD)
-b
latex
$(ALLSPHINXOPTS)
_
build/latex
@
echo
@
echo
"Build finished; the LaTeX files are in
.
build/latex."
@
echo
"Build finished; the LaTeX files are in
_
build/latex."
@
echo
"Run
\`
make all-pdf' or
\`
make all-ps' in that directory to"
\
"run these through (pdf)latex."
changes
:
mkdir
-p
.build/changes .
build/doctrees
$(SPHINXBUILD)
-b
changes
$(ALLSPHINXOPTS)
.
build/changes
mkdir
-p
_build/changes _
build/doctrees
$(SPHINXBUILD)
-b
changes
$(ALLSPHINXOPTS)
_
build/changes
@
echo
@
echo
"The overview file is in
.
build/changes."
@
echo
"The overview file is in
_
build/changes."
linkcheck
:
mkdir
-p
.build/linkcheck .
build/doctrees
$(SPHINXBUILD)
-b
linkcheck
$(ALLSPHINXOPTS)
.
build/linkcheck
mkdir
-p
_build/linkcheck _
build/doctrees
$(SPHINXBUILD)
-b
linkcheck
$(ALLSPHINXOPTS)
_
build/linkcheck
@
echo
@
echo
"Link check complete; look for any errors in the above output "
\
"or in
.
build/linkcheck/output.txt."
"or in
_
build/linkcheck/output.txt."
docs/WSGI.rst
0 → 100644
View file @
9d17e061
Running Zope2 as a WSGI Application
===================================
This document assumes you have installed Zope into a ``virtualenv`` (see
:doc:`INSTALL-virtualenv`).
Install the Supporting Software
-------------------------------
To run as a WSGI application, you need to install some additional software.
.. code-block:: sh
$ bin/pip install \
--trusted-host download.zope.org \
--index http://download.zope.org/Zope2/index/2.13.22/ \
repoze.who repoze.tm2 repoze.retry Paste PasteDeploy PasteScript
Collecting repoze.who
...
Successfully installed Paste-1.7.5.1 PasteDeploy-1.3.4 PasteScript-1.7.5 repoze.retry-1.2 repoze.tm2-1.0 repoze.who-2.0
Update the Zope Application Configuration
-----------------------------------------
The generated ``etc/zope.conf`` file assumes that Zope will be running
using the built-in ``ZServer``.
.. code-block:: sh
$ vim etc/zope.conf
Update the contents as follows.
.. code-block:: apacheconf
%define INSTANCE /path/to/virtualenv
instancehome $INSTANCE
.. note::
The ``%define instance /path/to/virtualenv`` element must
point to the environment: there is no "relative to this file" support
built in.
Set up logging for the application.
.. code-block:: apacheconf
<eventlog>
level info
<logfile>
path $INSTANCE/log/event.log
level info
</logfile>
</eventlog>
<logger access>
level WARN
<logfile>
path $INSTANCE/log/Z2.log
format %(message)s
</logfile>
</logger>
Configure the database (note that you could use ``ZEO`` or ``Relstorage``
rather than a bare ``FileStorage``):
.. code-block:: apacheconf
<zodb_db main>
# Main FileStorage database
<filestorage>
# See .../ZODB/component.xml for directives (sectiontype
# "filestorage").
path $INSTANCE/var/Data.fs
</filestorage>
mount-point /
</zodb_db>
<zodb_db temporary>
# Temporary storage database (for sessions)
<temporarystorage>
name temporary storage for sessioning
</temporarystorage>
mount-point /temp_folder
container-class Products.TemporaryFolder.TemporaryContainer
</zodb_db>
Because we will be running a separately-configured WSGI server, remove any
``<http-server>`` configuration from the file.
Create the WSGI Server Configuration
------------------------------------
.. code-block:: sh
$ vim etc/zope.wsgi
First, configure the "application" endpoint for Zope:
.. code-block:: ini
[app:zope]
use = egg:Zope2#main
zope_conf = %(here)s/zope.conf
Next, set up the WSGI middleware pipeline:
.. code-block:: ini
[pipeline:main]
pipeline =
egg:paste#evalerror
egg:repoze.retry#retry
egg:repoze.tm2#tm
zope
The middleware layers are "wrapped" around the application endpoint as follows:
- ``paste#evalerror`` is debugging middleware, which shows tracebacks for
errors raised from the application. It should **not** be configured for
production use.
- ``repoze.retry#retry`` is middleware which retries requests when retriable
exceptions are raised. By default, it retries 3 times, and only for
requests which raise ``ZODB.ConflictError``. See
http://repozeretry.rtfd.org/ for details on configuring it otherwise.
- ``repoze.tm2#tm`` is middleware which begins a new transaction for each
request, and then either aborts the transaction (if the request raises an
exception) or commits it (if not). See
http://repozetm2.rtfd.org/ for details on configuring it.
Finally, configure the WSGI server:
.. code-block:: ini
[server:main]
use = egg:paste#http
host = localhost
port = 8080
.. note::
Any server conforming to PEP 333/3333 should work, although the parameters
could change.
Set up the Admin User
---------------------
Before starting the WSGI server, run the ``addzope2user`` script to configure
the administrative user.
.. code-block:: sh
$ bin/addzope2user admin <yourpasswordhere>
No handlers could be found for logger "ZODB.FileStorage"
User admin created.
Start the WSGI Server
---------------------
.. code-block:: sh
$ bin/paster serve etc/zope.wsgi
Starting server in PID 24934.
serving on http://127.0.0.1:8080
Running Other Applications in the same WSGI Server Process
----------------------------------------------------------
You can use any of the normal ``Paste`` WSGI features to combine Zope and
other WSGI applications inside the same server process. E.g., the following
configuration uses the
`composite application <http://pythonpaste.org/deploy/#composite-applications>`_
support offered by ``PasteDeploy`` to host Zope at the ``/`` prefix,
with static files served from disk at ``/static``:
.. code-block:: ini
[app:zope-app]
use = egg:Zope2#main
zope_conf = %(here)s/zope.conf
[pipeline:zope-pipeline]
pipeline =
egg:paste#evalerror
egg:repoze.retry#retry
egg:repoze.tm2#tm
zope-app
[app:static]
use = egg:Paste#static
document_root = %(here)s/static
[composite:main]
use = egg:Paste#urlmap
/ = zope-pipeline
/static = static
docs/index.rst
View file @
9d17e061
...
...
@@ -6,13 +6,16 @@ Contents:
.. toctree::
:maxdepth: 2
WHATSNEW.rst
INSTALL.rst
operation.rst
USERS.rst
SECURITY.rst
SETUID.rst
SIGNALS.rst
DEBUGGING.rst
maintenance.rst
changes.rst
WHATSNEW
INSTALL
INSTALL-buildout
INSTALL-virtualenv
operation
WSGI
USERS
SECURITY
SETUID
SIGNALS
DEBUGGING
maintenance
changes
docs/operation.rst
View file @
9d17e061
...
...
@@ -4,8 +4,9 @@ Configuring and Running Zope
.. highlight:: bash
After installing Zope and create a server instance (see :doc:`INSTALL`), the
end result is configured and operated as follows.
Whichever method you used to install Zope and create a server instance (see
:doc:`INSTALL-buildout` and :doc:`INSTALL-virtualenv`), the end result is
configured and operated the same way.
Configuring Zope
...
...
src/ZPublisher/BaseRequest.py
View file @
9d17e061
...
...
@@ -206,6 +206,7 @@ class BaseRequest:
def
__init__
(
self
,
other
=
None
,
**
kw
):
"""The constructor is not allowed to raise errors
"""
self
.
__doc__
=
None
# Make BaseRequest objects unpublishable
if
other
is
None
:
other
=
kw
else
:
other
.
update
(
kw
)
self
.
other
=
other
...
...
@@ -276,6 +277,9 @@ class BaseRequest:
raise
KeyError
,
key
return
v
def
__bobo_traverse__
(
self
,
name
):
raise
KeyError
(
name
)
def
__getattr__
(
self
,
key
,
default
=
_marker
):
v
=
self
.
get
(
key
,
default
)
if
v
is
_marker
:
...
...
src/ZPublisher/HTTPRequest.py
View file @
9d17e061
...
...
@@ -313,6 +313,7 @@ class HTTPRequest(BaseRequest):
self
.
_locale
=
locales
.
getLocale
(
None
,
None
,
None
)
def
__init__
(
self
,
stdin
,
environ
,
response
,
clean
=
0
):
self
.
__doc__
=
None
# Make HTTPRequest objects unpublishable
self
.
_orig_env
=
environ
# Avoid the overhead of scrubbing the environment in the
# case of request cloning for traversal purposes. If the
...
...
src/ZPublisher/tests/testBaseRequest.py
View file @
9d17e061
...
...
@@ -189,6 +189,19 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory):
folder
=
root
.
_setObject
(
'folder'
,
self
.
_makeBasicObject
())
return
root
,
folder
def
test_no_docstring_on_instance
(
self
):
root
,
folder
=
self
.
_makeRootAndFolder
()
r
=
self
.
_makeOne
(
root
)
self
.
assertTrue
(
r
.
__doc__
is
None
)
def
test___bobo_traverse___raises
(
self
):
root
,
folder
=
self
.
_makeRootAndFolder
()
folder
.
_setObject
(
'objBasic'
,
self
.
_makeBasicObject
())
r
=
self
.
_makeOne
(
root
)
self
.
assertRaises
(
KeyError
,
r
.
__bobo_traverse__
,
'REQUEST'
)
self
.
assertRaises
(
KeyError
,
r
.
__bobo_traverse__
,
'BODY'
)
self
.
assertRaises
(
KeyError
,
r
.
__bobo_traverse__
,
'BODYFILE'
)
def
test_traverse_basic
(
self
):
root
,
folder
=
self
.
_makeRootAndFolder
()
folder
.
_setObject
(
'objBasic'
,
self
.
_makeBasicObject
())
...
...
@@ -468,7 +481,7 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory):
self
.
assertRaises
(
NotFound
,
r
.
traverse
,
'not_found'
)
class
Test
BaseRequestZope3Views
(
unittest
.
TestCase
,
BaseRequest_factory
):
class
Test
RequestZope3ViewsBase
(
unittest
.
TestCase
,
BaseRequest_factory
):
_dummy_interface
=
None
...
...
@@ -479,13 +492,27 @@ class TestBaseRequestZope3Views(unittest.TestCase, BaseRequest_factory):
def
_makeOne
(
self
,
root
):
from
zope.interface
import
directlyProvides
from
zope.publisher.browser
import
IDefaultBrowserLayer
request
=
super
(
Test
BaseRequestZope3Views
,
self
).
_makeOne
(
root
)
request
=
super
(
Test
RequestZope3ViewsBase
,
self
).
_makeOne
(
root
)
# The request needs to implement the proper interface
directlyProvides
(
request
,
IDefaultBrowserLayer
)
return
request
def
_makeDummyAclUsers
(
self
):
from
Acquisition
import
Implicit
from
AccessControl.ZopeSecurityPolicy
import
_noroles
class
AclUsers
(
Implicit
):
def
validate
(
self
,
request
,
auth
=
''
,
roles
=
_noroles
):
# always validate access as anonymous, good for checking
# if things are publishable regardless of authorization
from
AccessControl.SpecialUsers
import
nobody
return
nobody
.
__of__
(
self
)
acl_users
=
AclUsers
()
return
acl_users
def
_makeRootAndFolder
(
self
):
root
=
self
.
_makeBasicObject
()
root
.
__allow_groups__
=
self
.
_makeDummyAclUsers
()
folder
=
root
.
_setObject
(
'folder'
,
self
.
_makeDummyObject
(
'folder'
))
return
root
,
folder
...
...
@@ -613,6 +640,9 @@ class TestBaseRequestZope3Views(unittest.TestCase, BaseRequest_factory):
gsm
.
registerAdapter
(
name
,
(
self
.
_dummyInterface
(),
IBrowserRequest
),
IDefaultViewName
,
''
)
class
TestBaseRequestZope3Views
(
TestRequestZope3ViewsBase
):
def
test_traverse_view
(
self
):
#simple view
root
,
folder
=
self
.
_makeRootAndFolder
()
...
...
src/ZPublisher/tests/testHTTPRequest.py
View file @
9d17e061
import
unittest
from
ZPublisher.tests.testBaseRequest
import
TestRequestZope3ViewsBase
from
zope.testing.cleanup
import
cleanUp
...
...
@@ -15,7 +17,7 @@ class RecordTests(unittest.TestCase):
self
.
assertEqual
(
d
,
rec
.
__dict__
)
class
HTTPRequest
Tests
(
unittest
.
TestCase
):
class
HTTPRequest
FactoryMixin
(
object
):
def
tearDown
(
self
):
cleanUp
()
...
...
@@ -26,7 +28,7 @@ class HTTPRequestTests(unittest.TestCase):
def
_makeOne
(
self
,
stdin
=
None
,
environ
=
None
,
response
=
None
,
clean
=
1
):
from
StringIO
import
StringIO
from
ZPublisher
import
NotFound
from
ZPublisher
.HTTPResponse
import
HTTPResponse
if
stdin
is
None
:
stdin
=
StringIO
()
...
...
@@ -43,20 +45,12 @@ class HTTPRequestTests(unittest.TestCase):
environ
[
'SERVER_PORT'
]
=
'8080'
if
response
is
None
:
class
_FauxResponse
(
object
):
_auth
=
None
debug_mode
=
False
errmsg
=
'OK'
def
notFoundError
(
self
,
message
):
raise
NotFound
,
message
response
=
HTTPResponse
(
stdout
=
StringIO
())
def
exception
(
self
,
*
args
,
**
kw
):
pass
return
self
.
_getTargetClass
()(
stdin
,
environ
,
response
,
clean
)
response
=
_FauxResponse
()
return
self
.
_getTargetClass
()(
stdin
,
environ
,
response
,
clean
)
class
HTTPRequestTests
(
unittest
.
TestCase
,
HTTPRequestFactoryMixin
):
def
_processInputs
(
self
,
inputs
):
from
urllib
import
quote_plus
...
...
@@ -144,6 +138,19 @@ class HTTPRequestTests(unittest.TestCase):
"Key %s not correctly reproduced in tainted; expected %r, "
"got %r"
%
(
key
,
req
.
form
[
key
],
req
.
taintedform
[
key
]))
def
test_no_docstring_on_instance
(
self
):
env
=
{
'SERVER_NAME'
:
'testingharnas'
,
'SERVER_PORT'
:
'80'
}
req
=
self
.
_makeOne
(
environ
=
env
)
self
.
assertTrue
(
req
.
__doc__
is
None
)
def
test___bobo_traverse___raises
(
self
):
env
=
{
'SERVER_NAME'
:
'testingharnas'
,
'SERVER_PORT'
:
'80'
}
req
=
self
.
_makeOne
(
environ
=
env
)
self
.
assertRaises
(
KeyError
,
req
.
__bobo_traverse__
,
'REQUEST'
)
self
.
assertRaises
(
KeyError
,
req
.
__bobo_traverse__
,
'BODY'
)
self
.
assertRaises
(
KeyError
,
req
.
__bobo_traverse__
,
'BODYFILE'
)
self
.
assertRaises
(
KeyError
,
req
.
__bobo_traverse__
,
'RESPONSE'
)
def
test_processInputs_wo_query_string
(
self
):
env
=
{
'SERVER_NAME'
:
'testingharnas'
,
'SERVER_PORT'
:
'80'
}
req
=
self
.
_makeOne
(
environ
=
env
)
...
...
@@ -1078,6 +1085,34 @@ class HTTPRequestTests(unittest.TestCase):
req
.
_script
=
[
'foo'
,
'bar'
]
self
.
assertEquals
(
req
.
getVirtualRoot
(),
'/foo/bar'
)
class
TestHTTPRequestZope3Views
(
TestRequestZope3ViewsBase
,):
def
_makeOne
(
self
,
root
):
from
zope.interface
import
directlyProvides
from
zope.publisher.browser
import
IDefaultBrowserLayer
request
=
HTTPRequestFactoryMixin
().
_makeOne
()
request
[
'PARENTS'
]
=
[
root
]
# The request needs to implement the proper interface
directlyProvides
(
request
,
IDefaultBrowserLayer
)
return
request
def
test_no_traversal_of_view_request_attribute
(
self
):
# make sure views don't accidentally publish the 'request' attribute
from
ZPublisher
import
NotFound
root
,
_
=
self
.
_makeRootAndFolder
()
# make sure the view itself is traversable:
view
=
self
.
_makeOne
(
root
).
traverse
(
'folder/@@meth'
)
from
ZPublisher.HTTPRequest
import
HTTPRequest
self
.
assertEqual
(
view
.
request
.
__class__
,
HTTPRequest
,)
# but not the request:
self
.
assertRaises
(
NotFound
,
self
.
_makeOne
(
root
).
traverse
,
'folder/@@meth/request'
)
TEST_ENVIRON
=
{
'CONTENT_TYPE'
:
'multipart/form-data; boundary=12345'
,
'REQUEST_METHOD'
:
'POST'
,
...
...
@@ -1109,4 +1144,5 @@ def test_suite():
suite
=
unittest
.
TestSuite
()
suite
.
addTest
(
unittest
.
makeSuite
(
RecordTests
))
suite
.
addTest
(
unittest
.
makeSuite
(
HTTPRequestTests
))
suite
.
addTest
(
unittest
.
makeSuite
(
TestHTTPRequestZope3Views
))
return
suite
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment