Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
zope-container
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
Boxiang Sun
zope-container
Commits
160df616
Commit
160df616
authored
May 04, 2007
by
Christian Theune
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Moving code to satellite.
parent
df23e50e
Changes
57
Hide whitespace changes
Inline
Side-by-side
Showing
57 changed files
with
8828 additions
and
0 deletions
+8828
-0
src/zope/app/container/SETUP.cfg
src/zope/app/container/SETUP.cfg
+5
-0
src/zope/app/container/__init__.py
src/zope/app/container/__init__.py
+2
-0
src/zope/app/container/_zope_app_container_contained.c
src/zope/app/container/_zope_app_container_contained.c
+325
-0
src/zope/app/container/_zope_proxy_proxy.c
src/zope/app/container/_zope_proxy_proxy.c
+1098
-0
src/zope/app/container/browser/__init__.py
src/zope/app/container/browser/__init__.py
+2
-0
src/zope/app/container/browser/add.pt
src/zope/app/container/browser/add.pt
+58
-0
src/zope/app/container/browser/adding.py
src/zope/app/container/browser/adding.py
+197
-0
src/zope/app/container/browser/commontasks.pt
src/zope/app/container/browser/commontasks.pt
+41
-0
src/zope/app/container/browser/configure.zcml
src/zope/app/container/browser/configure.zcml
+20
-0
src/zope/app/container/browser/contents.pt
src/zope/app/container/browser/contents.pt
+193
-0
src/zope/app/container/browser/contents.py
src/zope/app/container/browser/contents.py
+460
-0
src/zope/app/container/browser/find.pt
src/zope/app/container/browser/find.pt
+22
-0
src/zope/app/container/browser/find.py
src/zope/app/container/browser/find.py
+42
-0
src/zope/app/container/browser/ftests/__init__.py
src/zope/app/container/browser/ftests/__init__.py
+2
-0
src/zope/app/container/browser/ftests/configure.zcml
src/zope/app/container/browser/ftests/configure.zcml
+16
-0
src/zope/app/container/browser/ftests/index.txt
src/zope/app/container/browser/ftests/index.txt
+18
-0
src/zope/app/container/browser/ftests/test_contents.py
src/zope/app/container/browser/ftests/test_contents.py
+351
-0
src/zope/app/container/browser/index.pt
src/zope/app/container/browser/index.pt
+66
-0
src/zope/app/container/browser/meta.zcml
src/zope/app/container/browser/meta.zcml
+16
-0
src/zope/app/container/browser/metaconfigure.py
src/zope/app/container/browser/metaconfigure.py
+89
-0
src/zope/app/container/browser/tests/__init__.py
src/zope/app/container/browser/tests/__init__.py
+2
-0
src/zope/app/container/browser/tests/test_adding.py
src/zope/app/container/browser/tests/test_adding.py
+585
-0
src/zope/app/container/browser/tests/test_contents.py
src/zope/app/container/browser/tests/test_contents.py
+338
-0
src/zope/app/container/browser/tests/test_directive.py
src/zope/app/container/browser/tests/test_directive.py
+294
-0
src/zope/app/container/btree.py
src/zope/app/container/btree.py
+67
-0
src/zope/app/container/configure.zcml
src/zope/app/container/configure.zcml
+88
-0
src/zope/app/container/constraints.py
src/zope/app/container/constraints.py
+461
-0
src/zope/app/container/constraints.txt
src/zope/app/container/constraints.txt
+97
-0
src/zope/app/container/contained.py
src/zope/app/container/contained.py
+859
-0
src/zope/app/container/dependency.py
src/zope/app/container/dependency.py
+45
-0
src/zope/app/container/directory.py
src/zope/app/container/directory.py
+62
-0
src/zope/app/container/find.py
src/zope/app/container/find.py
+89
-0
src/zope/app/container/ftesting.zcml
src/zope/app/container/ftesting.zcml
+57
-0
src/zope/app/container/ftests/__init__.py
src/zope/app/container/ftests/__init__.py
+2
-0
src/zope/app/container/ftests/ftesting.zcml
src/zope/app/container/ftests/ftesting.zcml
+57
-0
src/zope/app/container/ftests/test_view_permissions.py
src/zope/app/container/ftests/test_view_permissions.py
+103
-0
src/zope/app/container/i18n.py
src/zope/app/container/i18n.py
+22
-0
src/zope/app/container/interfaces.py
src/zope/app/container/interfaces.py
+301
-0
src/zope/app/container/ordered.py
src/zope/app/container/ordered.py
+303
-0
src/zope/app/container/sample.py
src/zope/app/container/sample.py
+91
-0
src/zope/app/container/size.py
src/zope/app/container/size.py
+41
-0
src/zope/app/container/testing.py
src/zope/app/container/testing.py
+7
-0
src/zope/app/container/tests/__init__.py
src/zope/app/container/tests/__init__.py
+2
-0
src/zope/app/container/tests/placelesssetup.py
src/zope/app/container/tests/placelesssetup.py
+25
-0
src/zope/app/container/tests/test_btree.py
src/zope/app/container/tests/test_btree.py
+39
-0
src/zope/app/container/tests/test_constraints.py
src/zope/app/container/tests/test_constraints.py
+34
-0
src/zope/app/container/tests/test_contained.py
src/zope/app/container/tests/test_contained.py
+327
-0
src/zope/app/container/tests/test_containertraversable.py
src/zope/app/container/tests/test_containertraversable.py
+70
-0
src/zope/app/container/tests/test_containertraverser.py
src/zope/app/container/tests/test_containertraverser.py
+94
-0
src/zope/app/container/tests/test_directory.py
src/zope/app/container/tests/test_directory.py
+39
-0
src/zope/app/container/tests/test_find.py
src/zope/app/container/tests/test_find.py
+174
-0
src/zope/app/container/tests/test_icontainer.py
src/zope/app/container/tests/test_icontainer.py
+319
-0
src/zope/app/container/tests/test_objectcopier.py
src/zope/app/container/tests/test_objectcopier.py
+211
-0
src/zope/app/container/tests/test_objectmover.py
src/zope/app/container/tests/test_objectmover.py
+227
-0
src/zope/app/container/tests/test_ordered.py
src/zope/app/container/tests/test_ordered.py
+101
-0
src/zope/app/container/tests/test_size.py
src/zope/app/container/tests/test_size.py
+70
-0
src/zope/app/container/traversal.py
src/zope/app/container/traversal.py
+102
-0
No files found.
src/zope/app/container/SETUP.cfg
0 → 100644
View file @
160df616
# Extension information for zpkg:
<extension _zope_app_container_contained>
source _zope_app_container_contained.c
</extension>
src/zope/app/container/__init__.py
0 → 100644
View file @
160df616
#
# This file is necessary to make this directory a package.
src/zope/app/container/_zope_app_container_contained.c
0 → 100644
View file @
160df616
/*############################################################################
#
# Copyright (c) 2003 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.
#
############################################################################*/
#define _ZOPE_APP_CONTAINER_CONTAINED_C "$Id$\n"
/* Contained Proxy Base class
Contained proxies provide __parent__ and __name__ attributes for
objects without them.
There is something strange and, possibly cool, going on here, wrt
persistence. To reuse the base proxy implementation we don't treat
the proxied object as part of the persistent state of the proxy.
This means that the proxy still operates as a proxy even if it is a
ghost.
The proxy will only be unghostified if you need to access one of the
attributes provided by the proxy.
*/
#include "Python.h"
#include "persistent/cPersistence.h"
static
PyObject
*
str_p_deactivate
;
typedef
struct
{
cPersistent_HEAD
PyObject
*
po_weaklist
;
PyObject
*
proxy_object
;
PyObject
*
__parent__
;
PyObject
*
__name__
;
}
ProxyObject
;
typedef
struct
{
PyTypeObject
*
proxytype
;
int
(
*
check
)(
PyObject
*
obj
);
PyObject
*
(
*
create
)(
PyObject
*
obj
);
PyObject
*
(
*
getobject
)(
PyObject
*
proxy
);
}
ProxyInterface
;
#define OBJECT(O) ((PyObject*)(O))
#define Proxy_GET_OBJECT(ob) (((ProxyObject *)(ob))->proxy_object)
#define CLEAR(O) \
if (O) {PyObject *clr__tmp = O; O = NULL; Py_DECREF(clr__tmp); }
/* Supress inclusion of the original proxy.h */
#define _proxy_H_ 1
/* Incude the proxy C source */
#include "_zope_proxy_proxy.c"
#define SPECIAL(NAME) ( \
*(NAME) == '_' && \
(((NAME)[1] == 'p' && (NAME)[2] == '_') \
|| \
((NAME)[1] == '_' && ( \
strcmp((NAME), "__parent__") == 0 \
|| \
strcmp((NAME), "__name__") == 0 \
|| \
strcmp((NAME), "__getstate__") == 0 \
|| \
strcmp((NAME), "__setstate__") == 0 \
|| \
strcmp((NAME), "__getnewargs__") == 0 \
|| \
strcmp((NAME), "__reduce__") == 0 \
|| \
strcmp((NAME), "__reduce_ex__") == 0 \
)) \
))
static
PyObject
*
CP_getattro
(
PyObject
*
self
,
PyObject
*
name
)
{
char
*
cname
;
cname
=
PyString_AsString
(
name
);
if
(
cname
==
NULL
)
return
NULL
;
if
(
SPECIAL
(
cname
))
/* delegate to persistent */
return
cPersistenceCAPI
->
pertype
->
tp_getattro
(
self
,
name
);
/* Use the wrapper version to delegate */
return
wrap_getattro
(
self
,
name
);
}
static
int
CP_setattro
(
PyObject
*
self
,
PyObject
*
name
,
PyObject
*
v
)
{
char
*
cname
;
cname
=
PyString_AsString
(
name
);
if
(
cname
==
NULL
)
return
-
1
;
if
(
SPECIAL
(
cname
))
/* delegate to persistent */
return
cPersistenceCAPI
->
pertype
->
tp_setattro
(
self
,
name
,
v
);
/* Use the wrapper version to delegate */
return
wrap_setattro
(
self
,
name
,
v
);
}
static
PyObject
*
CP_getstate
(
ProxyObject
*
self
)
{
return
Py_BuildValue
(
"OO"
,
self
->
__parent__
?
self
->
__parent__
:
Py_None
,
self
->
__name__
?
self
->
__name__
:
Py_None
);
}
static
PyObject
*
CP_getnewargs
(
ProxyObject
*
self
)
{
return
Py_BuildValue
(
"(O)"
,
self
->
proxy_object
);
}
static
PyObject
*
CP_setstate
(
ProxyObject
*
self
,
PyObject
*
state
)
{
PyObject
*
parent
,
*
name
;
if
(
!
PyArg_ParseTuple
(
state
,
"OO"
,
&
parent
,
&
name
))
return
NULL
;
CLEAR
(
self
->
__parent__
);
CLEAR
(
self
->
__name__
);
Py_INCREF
(
parent
);
Py_INCREF
(
name
);
self
->
__parent__
=
parent
;
self
->
__name__
=
name
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
CP_reduce
(
ProxyObject
*
self
)
{
PyObject
*
result
;
if
(
!
PER_USE
(
self
))
return
NULL
;
result
=
Py_BuildValue
(
"O(O)(OO)"
,
self
->
ob_type
,
self
->
proxy_object
,
self
->
__parent__
?
self
->
__parent__
:
Py_None
,
self
->
__name__
?
self
->
__name__
:
Py_None
);
PER_ALLOW_DEACTIVATION
(
self
);
return
result
;
}
static
PyObject
*
CP_reduce_ex
(
ProxyObject
*
self
,
PyObject
*
proto
)
{
return
CP_reduce
(
self
);
}
static
PyObject
*
CP__p_deactivate
(
ProxyObject
*
self
)
{
PyObject
*
result
;
result
=
PyObject_CallMethodObjArgs
(
OBJECT
(
cPersistenceCAPI
->
pertype
),
str_p_deactivate
,
self
,
NULL
);
if
(
result
==
NULL
)
return
NULL
;
if
(
self
->
jar
&&
self
->
oid
&&
self
->
state
==
cPersistent_UPTODATE_STATE
)
{
Py_XDECREF
(
self
->
__parent__
);
self
->
__parent__
=
NULL
;
Py_XDECREF
(
self
->
__name__
);
self
->
__name__
=
NULL
;
}
return
result
;
}
static
PyMethodDef
CP_methods
[]
=
{
{
"__getstate__"
,
(
PyCFunction
)
CP_getstate
,
METH_NOARGS
,
"Get the object state"
},
{
"__setstate__"
,
(
PyCFunction
)
CP_setstate
,
METH_O
,
"Set the object state"
},
{
"__getnewargs__"
,
(
PyCFunction
)
CP_getnewargs
,
METH_NOARGS
,
"Get the arguments that must be passed to __new__"
},
{
"__reduce__"
,
(
PyCFunction
)
CP_reduce
,
METH_NOARGS
,
"Reduce the object to constituent parts."
},
{
"__reduce_ex__"
,
(
PyCFunction
)
CP_reduce_ex
,
METH_O
,
"Reduce the object to constituent parts."
},
{
"_p_deactivate"
,
(
PyCFunction
)
CP__p_deactivate
,
METH_NOARGS
,
"Deactivate the object."
},
{
NULL
,
NULL
},
};
/* Code to access structure members by accessing attributes */
#include "structmember.h"
static
PyMemberDef
CP_members
[]
=
{
{
"__parent__"
,
T_OBJECT
,
offsetof
(
ProxyObject
,
__parent__
)},
{
"__name__"
,
T_OBJECT
,
offsetof
(
ProxyObject
,
__name__
)},
{
NULL
}
/* Sentinel */
};
static
int
CP_traverse
(
ProxyObject
*
self
,
visitproc
visit
,
void
*
arg
)
{
if
(
cPersistenceCAPI
->
pertype
->
tp_traverse
((
PyObject
*
)
self
,
visit
,
arg
)
<
0
)
return
-
1
;
if
(
self
->
proxy_object
!=
NULL
&&
visit
(
self
->
proxy_object
,
arg
)
<
0
)
return
-
1
;
if
(
self
->
__parent__
!=
NULL
&&
visit
(
self
->
__parent__
,
arg
)
<
0
)
return
-
1
;
if
(
self
->
__name__
!=
NULL
&&
visit
(
self
->
__name__
,
arg
)
<
0
)
return
-
1
;
return
0
;
}
static
int
CP_clear
(
ProxyObject
*
self
)
{
/* Drop references that may have created reference
cycles. Immutable objects do not have to define this method
since they can never directly create reference cycles. Note
that the object must still be valid after calling this
method (don't just call Py_DECREF() on a reference). The
collector will call this method if it detects that this
object is involved in a reference cycle.
*/
if
(
cPersistenceCAPI
->
pertype
->
tp_clear
!=
NULL
)
cPersistenceCAPI
->
pertype
->
tp_clear
((
PyObject
*
)
self
);
CLEAR
(
self
->
proxy_object
);
CLEAR
(
self
->
__parent__
);
CLEAR
(
self
->
__name__
);
return
0
;
}
static
void
CP_dealloc
(
ProxyObject
*
self
)
{
if
(
self
->
po_weaklist
!=
NULL
)
PyObject_ClearWeakRefs
((
PyObject
*
)
self
);
CLEAR
(
self
->
proxy_object
);
CLEAR
(
self
->
__parent__
);
CLEAR
(
self
->
__name__
);
cPersistenceCAPI
->
pertype
->
tp_dealloc
((
PyObject
*
)
self
);
}
#ifndef PyMODINIT_FUNC
/* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
init_zope_app_container_contained
(
void
)
{
PyObject
*
m
;
str_p_deactivate
=
PyString_FromString
(
"_p_deactivate"
);
if
(
str_p_deactivate
==
NULL
)
return
;
/* Try to fake out compiler nag function */
if
(
0
)
init_zope_proxy_proxy
();
m
=
Py_InitModule3
(
"_zope_app_container_contained"
,
module_functions
,
module___doc__
);
if
(
m
==
NULL
)
return
;
if
(
empty_tuple
==
NULL
)
empty_tuple
=
PyTuple_New
(
0
);
/* Initialize the PyPersist_C_API and the type objects. */
cPersistenceCAPI
=
PyCObject_Import
(
"persistent.cPersistence"
,
"CAPI"
);
if
(
cPersistenceCAPI
==
NULL
)
return
;
ProxyType
.
tp_name
=
"zope.app.container.contained.ContainedProxyBase"
;
ProxyType
.
ob_type
=
&
PyType_Type
;
ProxyType
.
tp_base
=
cPersistenceCAPI
->
pertype
;
ProxyType
.
tp_getattro
=
CP_getattro
;
ProxyType
.
tp_setattro
=
CP_setattro
;
ProxyType
.
tp_members
=
CP_members
;
ProxyType
.
tp_methods
=
CP_methods
;
ProxyType
.
tp_traverse
=
(
traverseproc
)
CP_traverse
;
ProxyType
.
tp_clear
=
(
inquiry
)
CP_clear
;
ProxyType
.
tp_dealloc
=
(
destructor
)
CP_dealloc
;
ProxyType
.
tp_weaklistoffset
=
offsetof
(
ProxyObject
,
po_weaklist
);
if
(
PyType_Ready
(
&
ProxyType
)
<
0
)
return
;
Py_INCREF
(
&
ProxyType
);
PyModule_AddObject
(
m
,
"ContainedProxyBase"
,
(
PyObject
*
)
&
ProxyType
);
}
src/zope/app/container/_zope_proxy_proxy.c
0 → 100644
View file @
160df616
/*############################################################################
#
# 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.
#
############################################################################*/
/*
* This file is also used as a really extensive macro in
* ../app/container/_zope_app_container_contained.c. If you need to
* change this file, you need to "svn copy" it to ../app/container/.
*
* This approach is taken to allow the sources for the two packages
* to be compilable when the relative locations of these aren't
* related in the same way as they are in a checkout.
*
* This will be revisited in the future, but works for now.
*/
#include "Python.h"
#include "modsupport.h"
#define PROXY_MODULE
#include "zope.proxy/proxy.h"
static
PyTypeObject
ProxyType
;
#define Proxy_Check(wrapper) (PyObject_TypeCheck((wrapper), &ProxyType))
static
PyObject
*
empty_tuple
=
NULL
;
/*
* Slot methods.
*/
static
PyObject
*
wrap_new
(
PyTypeObject
*
type
,
PyObject
*
args
,
PyObject
*
kwds
)
{
PyObject
*
result
=
NULL
;
PyObject
*
object
;
if
(
PyArg_UnpackTuple
(
args
,
"__new__"
,
1
,
1
,
&
object
))
{
if
(
kwds
!=
NULL
&&
PyDict_Size
(
kwds
)
!=
0
)
{
PyErr_SetString
(
PyExc_TypeError
,
"proxy.__new__ does not accept keyword args"
);
return
NULL
;
}
result
=
PyType_GenericNew
(
type
,
args
,
kwds
);
if
(
result
!=
NULL
)
{
ProxyObject
*
wrapper
=
(
ProxyObject
*
)
result
;
Py_INCREF
(
object
);
wrapper
->
proxy_object
=
object
;
}
}
return
result
;
}
static
int
wrap_init
(
PyObject
*
self
,
PyObject
*
args
,
PyObject
*
kwds
)
{
int
result
=
-
1
;
PyObject
*
object
;
if
(
PyArg_UnpackTuple
(
args
,
"__init__"
,
1
,
1
,
&
object
))
{
ProxyObject
*
wrapper
=
(
ProxyObject
*
)
self
;
if
(
kwds
!=
NULL
&&
PyDict_Size
(
kwds
)
!=
0
)
{
PyErr_SetString
(
PyExc_TypeError
,
"proxy.__init__ does not accept keyword args"
);
return
-
1
;
}
/* If the object in this proxy is not the one we
* received in args, replace it with the new one.
*/
if
(
wrapper
->
proxy_object
!=
object
)
{
PyObject
*
temp
=
wrapper
->
proxy_object
;
Py_INCREF
(
object
);
wrapper
->
proxy_object
=
object
;
Py_DECREF
(
temp
);
}
result
=
0
;
}
return
result
;
}
static
int
wrap_traverse
(
PyObject
*
self
,
visitproc
visit
,
void
*
arg
)
{
PyObject
*
ob
=
Proxy_GET_OBJECT
(
self
);
if
(
ob
!=
NULL
)
return
visit
(
ob
,
arg
);
else
return
0
;
}
static
int
wrap_clear
(
PyObject
*
self
)
{
ProxyObject
*
proxy
=
(
ProxyObject
*
)
self
;
PyObject
*
temp
=
proxy
->
proxy_object
;
if
(
temp
!=
NULL
)
{
proxy
->
proxy_object
=
NULL
;
Py_DECREF
(
temp
);
}
return
0
;
}
static
PyObject
*
wrap_richcompare
(
PyObject
*
self
,
PyObject
*
other
,
int
op
)
{
if
(
Proxy_Check
(
self
))
{
self
=
Proxy_GET_OBJECT
(
self
);
}
else
{
other
=
Proxy_GET_OBJECT
(
other
);
}
return
PyObject_RichCompare
(
self
,
other
,
op
);
}
static
PyObject
*
wrap_iter
(
PyObject
*
self
)
{
return
PyObject_GetIter
(
Proxy_GET_OBJECT
(
self
));
}
static
PyObject
*
wrap_iternext
(
PyObject
*
self
)
{
return
PyIter_Next
(
Proxy_GET_OBJECT
(
self
));
}
static
void
wrap_dealloc
(
PyObject
*
self
)
{
(
void
)
wrap_clear
(
self
);
self
->
ob_type
->
tp_free
(
self
);
}
/* A variant of _PyType_Lookup that doesn't look in ProxyType.
*
* If argument search_wrappertype is nonzero, we can look in WrapperType.
*/
PyObject
*
WrapperType_Lookup
(
PyTypeObject
*
type
,
PyObject
*
name
)
{
int
i
,
n
;
PyObject
*
mro
,
*
res
,
*
base
,
*
dict
;
/* Look in tp_dict of types in MRO */
mro
=
type
->
tp_mro
;
/* If mro is NULL, the type is either not yet initialized
by PyType_Ready(), or already cleared by type_clear().
Either way the safest thing to do is to return NULL. */
if
(
mro
==
NULL
)
return
NULL
;
assert
(
PyTuple_Check
(
mro
));
n
=
PyTuple_GET_SIZE
(
mro
)
-
1
;
/* We don't want to look at the last item, which is object. */
for
(
i
=
0
;
i
<
n
;
i
++
)
{
base
=
PyTuple_GET_ITEM
(
mro
,
i
);
if
(((
PyTypeObject
*
)
base
)
!=
&
ProxyType
)
{
if
(
PyClass_Check
(
base
))
dict
=
((
PyClassObject
*
)
base
)
->
cl_dict
;
else
{
assert
(
PyType_Check
(
base
));
dict
=
((
PyTypeObject
*
)
base
)
->
tp_dict
;
}
assert
(
dict
&&
PyDict_Check
(
dict
));
res
=
PyDict_GetItem
(
dict
,
name
);
if
(
res
!=
NULL
)
return
res
;
}
}
return
NULL
;
}
static
PyObject
*
wrap_getattro
(
PyObject
*
self
,
PyObject
*
name
)
{
PyObject
*
wrapped
;
PyObject
*
descriptor
;
PyObject
*
res
=
NULL
;
char
*
name_as_string
;
int
maybe_special_name
;
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
existing tp_getattro slots expect a string object as name
and we wouldn't want to break those. */
if
(
PyUnicode_Check
(
name
))
{
name
=
PyUnicode_AsEncodedString
(
name
,
NULL
,
NULL
);
if
(
name
==
NULL
)
return
NULL
;
}
else
#endif
if
(
!
PyString_Check
(
name
)){
PyErr_SetString
(
PyExc_TypeError
,
"attribute name must be string"
);
return
NULL
;
}
else
Py_INCREF
(
name
);
name_as_string
=
PyString_AS_STRING
(
name
);
wrapped
=
Proxy_GET_OBJECT
(
self
);
if
(
wrapped
==
NULL
)
{
PyErr_Format
(
PyExc_RuntimeError
,
"object is NULL; requested to get attribute '%s'"
,
name_as_string
);
goto
finally
;
}
maybe_special_name
=
name_as_string
[
0
]
==
'_'
&&
name_as_string
[
1
]
==
'_'
;
if
(
!
(
maybe_special_name
&&
strcmp
(
name_as_string
,
"__class__"
)
==
0
))
{
descriptor
=
WrapperType_Lookup
(
self
->
ob_type
,
name
);
if
(
descriptor
!=
NULL
)
{
if
(
PyType_HasFeature
(
descriptor
->
ob_type
,
Py_TPFLAGS_HAVE_CLASS
)
&&
descriptor
->
ob_type
->
tp_descr_get
!=
NULL
)
{
res
=
descriptor
->
ob_type
->
tp_descr_get
(
descriptor
,
self
,
(
PyObject
*
)
self
->
ob_type
);
}
else
{
Py_INCREF
(
descriptor
);
res
=
descriptor
;
}
goto
finally
;
}
}
res
=
PyObject_GetAttr
(
wrapped
,
name
);
finally:
Py_DECREF
(
name
);
return
res
;
}
static
int
wrap_setattro
(
PyObject
*
self
,
PyObject
*
name
,
PyObject
*
value
)
{
PyObject
*
wrapped
;
PyObject
*
descriptor
;
int
res
=
-
1
;
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
existing tp_setattro slots expect a string object as name
and we wouldn't want to break those. */
if
(
PyUnicode_Check
(
name
))
{
name
=
PyUnicode_AsEncodedString
(
name
,
NULL
,
NULL
);
if
(
name
==
NULL
)
return
-
1
;
}
else
#endif
if
(
!
PyString_Check
(
name
)){
PyErr_SetString
(
PyExc_TypeError
,
"attribute name must be string"
);
return
-
1
;
}
else
Py_INCREF
(
name
);
descriptor
=
WrapperType_Lookup
(
self
->
ob_type
,
name
);
if
(
descriptor
!=
NULL
)
{
if
(
PyType_HasFeature
(
descriptor
->
ob_type
,
Py_TPFLAGS_HAVE_CLASS
)
&&
descriptor
->
ob_type
->
tp_descr_set
!=
NULL
)
{
res
=
descriptor
->
ob_type
->
tp_descr_set
(
descriptor
,
self
,
value
);
}
else
{
PyErr_Format
(
PyExc_TypeError
,
"Tried to set attribute '%s' on wrapper, but it is not"
" a data descriptor"
,
PyString_AS_STRING
(
name
));
}
goto
finally
;
}
wrapped
=
Proxy_GET_OBJECT
(
self
);
if
(
wrapped
==
NULL
)
{
PyErr_Format
(
PyExc_RuntimeError
,
"object is NULL; requested to set attribute '%s'"
,
PyString_AS_STRING
(
name
));
goto
finally
;
}
res
=
PyObject_SetAttr
(
wrapped
,
name
,
value
);
finally:
Py_DECREF
(
name
);
return
res
;
}
static
int
wrap_print
(
PyObject
*
wrapper
,
FILE
*
fp
,
int
flags
)
{
return
PyObject_Print
(
Proxy_GET_OBJECT
(
wrapper
),
fp
,
flags
);
}
static
PyObject
*
wrap_str
(
PyObject
*
wrapper
)
{
return
PyObject_Str
(
Proxy_GET_OBJECT
(
wrapper
));
}
static
PyObject
*
wrap_repr
(
PyObject
*
wrapper
)
{
return
PyObject_Repr
(
Proxy_GET_OBJECT
(
wrapper
));
}
static
int
wrap_compare
(
PyObject
*
wrapper
,
PyObject
*
v
)
{
return
PyObject_Compare
(
Proxy_GET_OBJECT
(
wrapper
),
v
);
}
static
long
wrap_hash
(
PyObject
*
self
)
{
return
PyObject_Hash
(
Proxy_GET_OBJECT
(
self
));
}
static
PyObject
*
wrap_call
(
PyObject
*
self
,
PyObject
*
args
,
PyObject
*
kw
)
{
if
(
kw
)
return
PyEval_CallObjectWithKeywords
(
Proxy_GET_OBJECT
(
self
),
args
,
kw
);
else
return
PyObject_CallObject
(
Proxy_GET_OBJECT
(
self
),
args
);
}
/*
* Number methods
*/
/*
* Number methods.
*/
static
PyObject
*
call_int
(
PyObject
*
self
)
{
PyNumberMethods
*
nb
=
self
->
ob_type
->
tp_as_number
;
if
(
nb
==
NULL
||
nb
->
nb_int
==
NULL
)
{
PyErr_SetString
(
PyExc_TypeError
,
"object can't be converted to int"
);
return
NULL
;
}
return
nb
->
nb_int
(
self
);
}
static
PyObject
*
call_long
(
PyObject
*
self
)
{
PyNumberMethods
*
nb
=
self
->
ob_type
->
tp_as_number
;
if
(
nb
==
NULL
||
nb
->
nb_long
==
NULL
)
{
PyErr_SetString
(
PyExc_TypeError
,
"object can't be converted to long"
);
return
NULL
;
}
return
nb
->
nb_long
(
self
);
}
static
PyObject
*
call_float
(
PyObject
*
self
)
{
PyNumberMethods
*
nb
=
self
->
ob_type
->
tp_as_number
;
if
(
nb
==
NULL
||
nb
->
nb_float
==
NULL
)
{
PyErr_SetString
(
PyExc_TypeError
,
"object can't be converted to float"
);
return
NULL
;
}
return
nb
->
nb_float
(
self
);
}
static
PyObject
*
call_oct
(
PyObject
*
self
)
{
PyNumberMethods
*
nb
=
self
->
ob_type
->
tp_as_number
;
if
(
nb
==
NULL
||
nb
->
nb_oct
==
NULL
)
{
PyErr_SetString
(
PyExc_TypeError
,
"object can't be converted to oct"
);
return
NULL
;
}
return
nb
->
nb_oct
(
self
);
}
static
PyObject
*
call_hex
(
PyObject
*
self
)
{
PyNumberMethods
*
nb
=
self
->
ob_type
->
tp_as_number
;
if
(
nb
==
NULL
||
nb
->
nb_hex
==
NULL
)
{
PyErr_SetString
(
PyExc_TypeError
,
"object can't be converted to hex"
);
return
NULL
;
}
return
nb
->
nb_hex
(
self
);
}
static
PyObject
*
call_ipow
(
PyObject
*
self
,
PyObject
*
other
)
{
/* PyNumber_InPlacePower has three args. How silly. :-) */
return
PyNumber_InPlacePower
(
self
,
other
,
Py_None
);
}
typedef
PyObject
*
(
*
function1
)(
PyObject
*
);
static
PyObject
*
check1
(
ProxyObject
*
self
,
char
*
opname
,
function1
operation
)
{
PyObject
*
result
=
NULL
;
result
=
operation
(
Proxy_GET_OBJECT
(
self
));
#if 0
if (result != NULL)
/* ??? create proxy for result? */
;
#endif
return
result
;
}
static
PyObject
*
check2
(
PyObject
*
self
,
PyObject
*
other
,
char
*
opname
,
char
*
ropname
,
binaryfunc
operation
)
{
PyObject
*
result
=
NULL
;
PyObject
*
object
;
if
(
Proxy_Check
(
self
))
{
object
=
Proxy_GET_OBJECT
(
self
);
result
=
operation
(
object
,
other
);
}
else
if
(
Proxy_Check
(
other
))
{
object
=
Proxy_GET_OBJECT
(
other
);
result
=
operation
(
self
,
object
);
}
else
{
Py_INCREF
(
Py_NotImplemented
);
return
Py_NotImplemented
;
}
#if 0
if (result != NULL)
/* ??? create proxy for result? */
;
#endif
return
result
;
}
static
PyObject
*
check2i
(
ProxyObject
*
self
,
PyObject
*
other
,
char
*
opname
,
binaryfunc
operation
)
{
PyObject
*
result
=
NULL
;
PyObject
*
object
=
Proxy_GET_OBJECT
(
self
);
result
=
operation
(
object
,
other
);
if
(
result
==
object
)
{
/* If the operation was really carried out inplace,
don't create a new proxy, but use the old one. */
Py_INCREF
(
self
);
Py_DECREF
(
object
);
result
=
(
PyObject
*
)
self
;
}
#if 0
else if (result != NULL)
/* ??? create proxy for result? */
;
#endif
return
result
;
}
#define UNOP(NAME, CALL) \
static PyObject *wrap_##NAME(PyObject *self) \
{ return check1((ProxyObject *)self, "__"#NAME"__", CALL); }
#define BINOP(NAME, CALL) \
static PyObject *wrap_##NAME(PyObject *self, PyObject *other) \
{ return check2(self, other, "__"#NAME"__", "__r"#NAME"__", CALL); }
#define INPLACE(NAME, CALL) \
static PyObject *wrap_i##NAME(PyObject *self, PyObject *other) \
{ return check2i((ProxyObject *)self, other, "__i"#NAME"__", CALL); }
BINOP
(
add
,
PyNumber_Add
)
BINOP
(
sub
,
PyNumber_Subtract
)
BINOP
(
mul
,
PyNumber_Multiply
)
BINOP
(
div
,
PyNumber_Divide
)
BINOP
(
mod
,
PyNumber_Remainder
)
BINOP
(
divmod
,
PyNumber_Divmod
)
static
PyObject
*
wrap_pow
(
PyObject
*
self
,
PyObject
*
other
,
PyObject
*
modulus
)
{
PyObject
*
result
=
NULL
;
PyObject
*
object
;
if
(
Proxy_Check
(
self
))
{
object
=
Proxy_GET_OBJECT
(
self
);
result
=
PyNumber_Power
(
object
,
other
,
modulus
);
}
else
if
(
Proxy_Check
(
other
))
{
object
=
Proxy_GET_OBJECT
(
other
);
result
=
PyNumber_Power
(
self
,
object
,
modulus
);
}
else
if
(
modulus
!=
NULL
&&
Proxy_Check
(
modulus
))
{
object
=
Proxy_GET_OBJECT
(
modulus
);
result
=
PyNumber_Power
(
self
,
other
,
modulus
);
}
else
{
Py_INCREF
(
Py_NotImplemented
);
return
Py_NotImplemented
;
}
return
result
;
}
BINOP
(
lshift
,
PyNumber_Lshift
)
BINOP
(
rshift
,
PyNumber_Rshift
)
BINOP
(
and
,
PyNumber_And
)
BINOP
(
xor
,
PyNumber_Xor
)
BINOP
(
or
,
PyNumber_Or
)
static
int
wrap_coerce
(
PyObject
**
p_self
,
PyObject
**
p_other
)
{
PyObject
*
self
=
*
p_self
;
PyObject
*
other
=
*
p_other
;
PyObject
*
object
;
PyObject
*
left
;
PyObject
*
right
;
int
r
;
assert
(
Proxy_Check
(
self
));
object
=
Proxy_GET_OBJECT
(
self
);
left
=
object
;
right
=
other
;
r
=
PyNumber_CoerceEx
(
&
left
,
&
right
);
if
(
r
!=
0
)
return
r
;
/* Now left and right have been INCREF'ed. Any new value that
comes out is proxied; any unchanged value is left unchanged. */
if
(
left
==
object
)
{
/* Keep the old proxy */
Py_INCREF
(
self
);
Py_DECREF
(
left
);
left
=
self
;
}
#if 0
else {
/* ??? create proxy for left? */
}
if (right != other) {
/* ??? create proxy for right? */
}
#endif
*
p_self
=
left
;
*
p_other
=
right
;
return
0
;
}
UNOP
(
neg
,
PyNumber_Negative
)
UNOP
(
pos
,
PyNumber_Positive
)
UNOP
(
abs
,
PyNumber_Absolute
)
UNOP
(
invert
,
PyNumber_Invert
)
UNOP
(
int
,
call_int
)
UNOP
(
long
,
call_long
)
UNOP
(
float
,
call_float
)
UNOP
(
oct
,
call_oct
)
UNOP
(
hex
,
call_hex
)
INPLACE
(
add
,
PyNumber_InPlaceAdd
)
INPLACE
(
sub
,
PyNumber_InPlaceSubtract
)
INPLACE
(
mul
,
PyNumber_InPlaceMultiply
)
INPLACE
(
div
,
PyNumber_InPlaceDivide
)
INPLACE
(
mod
,
PyNumber_InPlaceRemainder
)
INPLACE
(
pow
,
call_ipow
)
INPLACE
(
lshift
,
PyNumber_InPlaceLshift
)
INPLACE
(
rshift
,
PyNumber_InPlaceRshift
)
INPLACE
(
and
,
PyNumber_InPlaceAnd
)
INPLACE
(
xor
,
PyNumber_InPlaceXor
)
INPLACE
(
or
,
PyNumber_InPlaceOr
)
BINOP
(
floordiv
,
PyNumber_FloorDivide
)
BINOP
(
truediv
,
PyNumber_TrueDivide
)
INPLACE
(
floordiv
,
PyNumber_InPlaceFloorDivide
)
INPLACE
(
truediv
,
PyNumber_InPlaceTrueDivide
)
static
int
wrap_nonzero
(
PyObject
*
self
)
{
return
PyObject_IsTrue
(
Proxy_GET_OBJECT
(
self
));
}
/*
* Sequence methods
*/
static
int
wrap_length
(
PyObject
*
self
)
{
return
PyObject_Length
(
Proxy_GET_OBJECT
(
self
));
}
static
PyObject
*
wrap_slice
(
PyObject
*
self
,
int
start
,
int
end
)
{
return
PySequence_GetSlice
(
Proxy_GET_OBJECT
(
self
),
start
,
end
);
}
static
int
wrap_ass_slice
(
PyObject
*
self
,
int
i
,
int
j
,
PyObject
*
value
)
{
return
PySequence_SetSlice
(
Proxy_GET_OBJECT
(
self
),
i
,
j
,
value
);
}
static
int
wrap_contains
(
PyObject
*
self
,
PyObject
*
value
)
{
return
PySequence_Contains
(
Proxy_GET_OBJECT
(
self
),
value
);
}
/*
* Mapping methods
*/
static
PyObject
*
wrap_getitem
(
PyObject
*
wrapper
,
PyObject
*
v
)
{
return
PyObject_GetItem
(
Proxy_GET_OBJECT
(
wrapper
),
v
);
}
static
int
wrap_setitem
(
PyObject
*
self
,
PyObject
*
key
,
PyObject
*
value
)
{
if
(
value
==
NULL
)
return
PyObject_DelItem
(
Proxy_GET_OBJECT
(
self
),
key
);
else
return
PyObject_SetItem
(
Proxy_GET_OBJECT
(
self
),
key
,
value
);
}
/*
* Normal methods
*/
static
char
reduce__doc__
[]
=
"__reduce__()
\n
"
"Raise an exception; this prevents proxies from being picklable by
\n
"
"default, even if the underlying object is picklable."
;
static
PyObject
*
wrap_reduce
(
PyObject
*
self
)
{
PyObject
*
pickle_error
=
NULL
;
PyObject
*
pickle
=
PyImport_ImportModule
(
"pickle"
);
if
(
pickle
==
NULL
)
PyErr_Clear
();
else
{
pickle_error
=
PyObject_GetAttrString
(
pickle
,
"PicklingError"
);
if
(
pickle_error
==
NULL
)
PyErr_Clear
();
}
if
(
pickle_error
==
NULL
)
{
pickle_error
=
PyExc_RuntimeError
;
Py_INCREF
(
pickle_error
);
}
PyErr_SetString
(
pickle_error
,
"proxy instances cannot be pickled"
);
Py_DECREF
(
pickle_error
);
return
NULL
;
}
static
PyNumberMethods
wrap_as_number
=
{
wrap_add
,
/* nb_add */
wrap_sub
,
/* nb_subtract */
wrap_mul
,
/* nb_multiply */
wrap_div
,
/* nb_divide */
wrap_mod
,
/* nb_remainder */
wrap_divmod
,
/* nb_divmod */
wrap_pow
,
/* nb_power */
wrap_neg
,
/* nb_negative */
wrap_pos
,
/* nb_positive */
wrap_abs
,
/* nb_absolute */
wrap_nonzero
,
/* nb_nonzero */
wrap_invert
,
/* nb_invert */
wrap_lshift
,
/* nb_lshift */
wrap_rshift
,
/* nb_rshift */
wrap_and
,
/* nb_and */
wrap_xor
,
/* nb_xor */
wrap_or
,
/* nb_or */
wrap_coerce
,
/* nb_coerce */
wrap_int
,
/* nb_int */
wrap_long
,
/* nb_long */
wrap_float
,
/* nb_float */
wrap_oct
,
/* nb_oct */
wrap_hex
,
/* nb_hex */
/* Added in release 2.0 */
/* These require the Py_TPFLAGS_HAVE_INPLACEOPS flag */
wrap_iadd
,
/* nb_inplace_add */
wrap_isub
,
/* nb_inplace_subtract */
wrap_imul
,
/* nb_inplace_multiply */
wrap_idiv
,
/* nb_inplace_divide */
wrap_imod
,
/* nb_inplace_remainder */
(
ternaryfunc
)
wrap_ipow
,
/* nb_inplace_power */
wrap_ilshift
,
/* nb_inplace_lshift */
wrap_irshift
,
/* nb_inplace_rshift */
wrap_iand
,
/* nb_inplace_and */
wrap_ixor
,
/* nb_inplace_xor */
wrap_ior
,
/* nb_inplace_or */
/* Added in release 2.2 */
/* These require the Py_TPFLAGS_HAVE_CLASS flag */
wrap_floordiv
,
/* nb_floor_divide */
wrap_truediv
,
/* nb_true_divide */
wrap_ifloordiv
,
/* nb_inplace_floor_divide */
wrap_itruediv
,
/* nb_inplace_true_divide */
};
static
PySequenceMethods
wrap_as_sequence
=
{
wrap_length
,
/* sq_length */
0
,
/* sq_concat */
0
,
/* sq_repeat */
0
,
/* sq_item */
wrap_slice
,
/* sq_slice */
0
,
/* sq_ass_item */
wrap_ass_slice
,
/* sq_ass_slice */
wrap_contains
,
/* sq_contains */
};
static
PyMappingMethods
wrap_as_mapping
=
{
wrap_length
,
/* mp_length */
wrap_getitem
,
/* mp_subscript */
wrap_setitem
,
/* mp_ass_subscript */
};
static
PyMethodDef
wrap_methods
[]
=
{
{
"__reduce__"
,
(
PyCFunction
)
wrap_reduce
,
METH_NOARGS
,
reduce__doc__
},
{
NULL
,
NULL
},
};
/*
* Note that the numeric methods are not supported. This is primarily
* because of the way coercion-less operations are performed with
* new-style numbers; since we can't tell which side of the operation
* is 'self', we can't ensure we'd unwrap the right thing to perform
* the actual operation. We also can't afford to just unwrap both
* sides the way weakrefs do, since we don't know what semantics will
* be associated with the wrapper itself.
*/
statichere
PyTypeObject
ProxyType
=
{
PyObject_HEAD_INIT
(
NULL
)
/* PyObject_HEAD_INIT(&PyType_Type) */
0
,
"zope.proxy.ProxyBase"
,
sizeof
(
ProxyObject
),
0
,
wrap_dealloc
,
/* tp_dealloc */
wrap_print
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
wrap_compare
,
/* tp_compare */
wrap_repr
,
/* tp_repr */
&
wrap_as_number
,
/* tp_as_number */
&
wrap_as_sequence
,
/* tp_as_sequence */
&
wrap_as_mapping
,
/* tp_as_mapping */
wrap_hash
,
/* tp_hash */
wrap_call
,
/* tp_call */
wrap_str
,
/* tp_str */
wrap_getattro
,
/* tp_getattro */
wrap_setattro
,
/* tp_setattro */
0
,
/* tp_as_buffer */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_HAVE_GC
|
Py_TPFLAGS_CHECKTYPES
|
Py_TPFLAGS_BASETYPE
,
/* tp_flags */
0
,
/* tp_doc */
wrap_traverse
,
/* tp_traverse */
wrap_clear
,
/* tp_clear */
wrap_richcompare
,
/* tp_richcompare */
0
,
/* tp_weaklistoffset */
wrap_iter
,
/* tp_iter */
wrap_iternext
,
/* tp_iternext */
wrap_methods
,
/* tp_methods */
0
,
/* tp_members */
0
,
/* tp_getset */
0
,
/* tp_base */
0
,
/* tp_dict */
0
,
/* tp_descr_get */
0
,
/* tp_descr_set */
0
,
/* tp_dictoffset */
wrap_init
,
/* tp_init */
0
,
/* tp_alloc */
wrap_new
,
/* tp_new */
0
,
/*_PyObject_GC_Del,*/
/* tp_free */
};
static
PyObject
*
create_proxy
(
PyObject
*
object
)
{
PyObject
*
result
=
NULL
;
PyObject
*
args
;
args
=
PyTuple_New
(
1
);
if
(
args
!=
NULL
)
{
Py_INCREF
(
object
);
PyTuple_SET_ITEM
(
args
,
0
,
object
);
result
=
PyObject_CallObject
((
PyObject
*
)
&
ProxyType
,
args
);
Py_DECREF
(
args
);
}
return
result
;
}
static
int
api_check
(
PyObject
*
obj
)
{
return
obj
?
Proxy_Check
(
obj
)
:
0
;
}
static
PyObject
*
api_create
(
PyObject
*
object
)
{
if
(
object
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
"cannot create proxy around NULL"
);
return
NULL
;
}
return
create_proxy
(
object
);
}
static
PyObject
*
api_getobject
(
PyObject
*
proxy
)
{
if
(
proxy
==
NULL
)
{
PyErr_SetString
(
PyExc_RuntimeError
,
"cannot pass NULL to ProxyAPI.getobject()"
);
return
NULL
;
}
if
(
Proxy_Check
(
proxy
))
return
Proxy_GET_OBJECT
(
proxy
);
else
{
PyErr_Format
(
PyExc_TypeError
,
"expected proxy object, got %s"
,
proxy
->
ob_type
->
tp_name
);
return
NULL
;
}
}
static
ProxyInterface
wrapper_capi
=
{
&
ProxyType
,
api_check
,
api_create
,
api_getobject
,
};
static
PyObject
*
api_object
=
NULL
;
static
char
getobject__doc__
[]
=
"getProxiedObject(proxy) --> object
\n
"
"
\n
"
"Get the underlying object for proxy, or the object itself, if it is
\n
"
"not a proxy."
;
static
PyObject
*
wrapper_getobject
(
PyObject
*
unused
,
PyObject
*
obj
)
{
if
(
Proxy_Check
(
obj
))
obj
=
Proxy_GET_OBJECT
(
obj
);
if
(
obj
==
NULL
)
obj
=
Py_None
;
Py_INCREF
(
obj
);
return
obj
;
}
static
char
isProxy__doc__
[]
=
"Check whether the given object is a proxy
\n
"
"
\n
"
"If proxytype is not None, checkes whether the object is
\n
"
"proxied by the given proxytype.
\n
"
;
static
PyObject
*
wrapper_isProxy
(
PyObject
*
unused
,
PyObject
*
args
)
{
PyObject
*
obj
,
*
result
;
PyTypeObject
*
proxytype
=&
ProxyType
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|O!:isProxy"
,
&
obj
,
&
PyType_Type
,
&
proxytype
)
)
return
NULL
;
while
(
obj
&&
Proxy_Check
(
obj
))
{
if
(
PyObject_TypeCheck
(
obj
,
proxytype
))
{
result
=
Py_True
;
Py_INCREF
(
result
);
return
result
;
}
obj
=
Proxy_GET_OBJECT
(
obj
);
}
result
=
Py_False
;
Py_INCREF
(
result
);
return
result
;
}
static
char
removeAllProxies__doc__
[]
=
"removeAllProxies(proxy) --> object
\n
"
"
\n
"
"Get the proxied object with no proxies
\n
"
"
\n
"
"If obj is not a proxied object, return obj.
\n
"
"
\n
"
"The returned object has no proxies.
\n
"
;
static
PyObject
*
wrapper_removeAllProxies
(
PyObject
*
unused
,
PyObject
*
obj
)
{
while
(
obj
&&
Proxy_Check
(
obj
))
obj
=
Proxy_GET_OBJECT
(
obj
);
if
(
obj
==
NULL
)
obj
=
Py_None
;
Py_INCREF
(
obj
);
return
obj
;
}
static
char
sameProxiedObjects__doc__
[]
=
"Check whether two objects are the same or proxies of the same object"
;
static
PyObject
*
wrapper_sameProxiedObjects
(
PyObject
*
unused
,
PyObject
*
args
)
{
PyObject
*
ob1
,
*
ob2
;
if
(
!
PyArg_ParseTuple
(
args
,
"OO:sameProxiedObjects"
,
&
ob1
,
&
ob2
))
return
NULL
;
while
(
ob1
&&
Proxy_Check
(
ob1
))
ob1
=
Proxy_GET_OBJECT
(
ob1
);
while
(
ob2
&&
Proxy_Check
(
ob2
))
ob2
=
Proxy_GET_OBJECT
(
ob2
);
if
(
ob1
==
ob2
)
ob1
=
Py_True
;
else
ob1
=
Py_False
;
Py_INCREF
(
ob1
);
return
ob1
;
}
static
char
queryProxy__doc__
[]
=
"Look for a proxy of the given type around the object
\n
"
"
\n
"
"If no such proxy can be found, return the default.
\n
"
;
static
PyObject
*
wrapper_queryProxy
(
PyObject
*
unused
,
PyObject
*
args
)
{
PyObject
*
obj
,
*
result
=
Py_None
;
PyTypeObject
*
proxytype
=&
ProxyType
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|O!O:queryProxy"
,
&
obj
,
&
PyType_Type
,
&
proxytype
,
&
result
)
)
return
NULL
;
while
(
obj
&&
Proxy_Check
(
obj
))
{
if
(
PyObject_TypeCheck
(
obj
,
proxytype
))
{
Py_INCREF
(
obj
);
return
obj
;
}
obj
=
Proxy_GET_OBJECT
(
obj
);
}
Py_INCREF
(
result
);
return
result
;
}
static
char
queryInnerProxy__doc__
[]
=
"Look for the inner-most proxy of the given type around the object
\n
"
"
\n
"
"If no such proxy can be found, return the default.
\n
"
"
\n
"
"If there is such a proxy, return the inner-most one.
\n
"
;
static
PyObject
*
wrapper_queryInnerProxy
(
PyObject
*
unused
,
PyObject
*
args
)
{
PyObject
*
obj
,
*
result
=
Py_None
;
PyTypeObject
*
proxytype
=&
ProxyType
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|O!O:queryInnerProxy"
,
&
obj
,
&
PyType_Type
,
&
proxytype
,
&
result
)
)
return
NULL
;
while
(
obj
&&
Proxy_Check
(
obj
))
{
if
(
PyObject_TypeCheck
(
obj
,
proxytype
))
result
=
obj
;
obj
=
Proxy_GET_OBJECT
(
obj
);
}
Py_INCREF
(
result
);
return
result
;
}
static
char
module___doc__
[]
=
"Association between an object, a context object, and a dictionary.
\n
\
\n
\
The context object and dictionary give additional context information
\n
\
associated with a reference to the basic object. The wrapper objects
\n
\
act as proxies for the original object."
;
static
PyMethodDef
module_functions
[]
=
{
{
"getProxiedObject"
,
wrapper_getobject
,
METH_O
,
getobject__doc__
},
{
"isProxy"
,
wrapper_isProxy
,
METH_VARARGS
,
isProxy__doc__
},
{
"sameProxiedObjects"
,
wrapper_sameProxiedObjects
,
METH_VARARGS
,
sameProxiedObjects__doc__
},
{
"queryProxy"
,
wrapper_queryProxy
,
METH_VARARGS
,
queryProxy__doc__
},
{
"queryInnerProxy"
,
wrapper_queryInnerProxy
,
METH_VARARGS
,
queryInnerProxy__doc__
},
{
"removeAllProxies"
,
wrapper_removeAllProxies
,
METH_O
,
removeAllProxies__doc__
},
{
NULL
}
};
void
init_zope_proxy_proxy
(
void
)
{
PyObject
*
m
=
Py_InitModule3
(
"_zope_proxy_proxy"
,
module_functions
,
module___doc__
);
if
(
m
==
NULL
)
return
;
if
(
empty_tuple
==
NULL
)
empty_tuple
=
PyTuple_New
(
0
);
ProxyType
.
tp_free
=
_PyObject_GC_Del
;
if
(
PyType_Ready
(
&
ProxyType
)
<
0
)
return
;
Py_INCREF
(
&
ProxyType
);
PyModule_AddObject
(
m
,
"ProxyBase"
,
(
PyObject
*
)
&
ProxyType
);
if
(
api_object
==
NULL
)
{
api_object
=
PyCObject_FromVoidPtr
(
&
wrapper_capi
,
NULL
);
if
(
api_object
==
NULL
)
return
;
}
Py_INCREF
(
api_object
);
PyModule_AddObject
(
m
,
"_CAPI"
,
api_object
);
}
src/zope/app/container/browser/__init__.py
0 → 100644
View file @
160df616
#
# This file is necessary to make this directory a package.
src/zope/app/container/browser/add.pt
0 → 100644
View file @
160df616
<html
metal:use-macro=
"context/@@standard_macros/addingdialog"
i18n:domain=
"zope"
>
<body>
<div
metal:fill-slot=
"body"
>
<form
method=
"post"
action=
"action.html"
>
<table
class=
"TypeListing"
cellpadding=
"3"
>
<tal:block
define=
"title view/title | nothing"
>
<caption
tal:condition=
"title"
tal:content=
"title"
i18n:translate=
""
>
Inserted title
</caption>
<caption
tal:condition=
"not:title"
i18n:translate=
""
>
Add Content
</caption>
</tal:block>
<tbody
tal:define=
"infos view/addingInfo"
>
<tr
tal:repeat=
"info infos"
>
<td
class=
"Selector"
>
<input
type=
"radio"
name=
"type_name"
tal:attributes=
"value info/action;
id info/action;
checked python:len(infos)==1"
/>
</td>
<td
class=
"TypeName"
>
<label
style=
"font-weight: bold;"
tal:attributes=
"for info/action"
>
<span
tal:replace=
"info/title"
i18n:translate=
""
>
Folder
</span>
</label>
<div
class=
"TypeDescription"
tal:content=
"info/description"
i18n:translate=
""
>
Folders are generic containers for content, including other
folders.
</div>
</td>
</tr>
<tr>
<td><br
/></td>
<td><input
type=
"text"
name=
"id"
tal:condition=
"view/nameAllowed"
tal:attributes=
"value request/id | nothing"
/>
<input
type=
"submit"
name=
"add"
value=
" Add "
i18n:attributes=
"value add-button"
/>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</body>
</html>
src/zope/app/container/browser/adding.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
"""Adding View
The Adding View is used to add new objects to a container. It is sort of a
factory screen.
$Id$
"""
__docformat__
=
'restructuredtext'
import
zope.security.checker
from
zope.component.interfaces
import
IFactory
from
zope.event
import
notify
from
zope.interface
import
implements
from
zope.publisher.interfaces
import
IPublishTraverse
from
zope.publisher.browser
import
BrowserView
from
zope.security.proxy
import
removeSecurityProxy
from
zope.exceptions.interfaces
import
UserError
from
zope.location
import
LocationProxy
from
zope.lifecycleevent
import
ObjectCreatedEvent
from
zope.app.container.interfaces
import
IAdding
,
INameChooser
from
zope.app.container.interfaces
import
IContainerNamesContainer
from
zope.app.container.constraints
import
checkFactory
,
checkObject
from
zope.app
import
zapi
from
zope.app.container.i18n
import
ZopeMessageFactory
as
_
from
zope.app.pagetemplate.viewpagetemplatefile
import
ViewPageTemplateFile
from
zope.app.publisher.browser.menu
import
getMenu
class
Adding
(
BrowserView
):
implements
(
IAdding
,
IPublishTraverse
)
def
add
(
self
,
content
):
"""See zope.app.container.interfaces.IAdding
"""
container
=
self
.
context
name
=
self
.
contentName
chooser
=
INameChooser
(
container
)
# check precondition
checkObject
(
container
,
name
,
content
)
if
IContainerNamesContainer
.
providedBy
(
container
):
# The container picks its own names.
# We need to ask it to pick one.
name
=
chooser
.
chooseName
(
self
.
contentName
or
''
,
content
)
else
:
request
=
self
.
request
name
=
request
.
get
(
'add_input_name'
,
name
)
if
name
is
None
:
name
=
chooser
.
chooseName
(
self
.
contentName
or
''
,
content
)
elif
name
==
''
:
name
=
chooser
.
chooseName
(
''
,
content
)
chooser
.
checkName
(
name
,
content
)
container
[
name
]
=
content
self
.
contentName
=
name
# Set the added object Name
return
container
[
name
]
contentName
=
None
# usually set by Adding traverser
def
nextURL
(
self
):
"""See zope.app.container.interfaces.IAdding"""
return
zapi
.
absoluteURL
(
self
.
context
,
self
.
request
)
+
'/@@contents.html'
# set in BrowserView.__init__
request
=
None
context
=
None
def
publishTraverse
(
self
,
request
,
name
):
"""See zope.app.container.interfaces.IAdding"""
if
'='
in
name
:
view_name
,
content_name
=
name
.
split
(
"="
,
1
)
self
.
contentName
=
content_name
if
view_name
.
startswith
(
'@@'
):
view_name
=
view_name
[
2
:]
return
zapi
.
getMultiAdapter
((
self
,
request
),
name
=
view_name
)
if
name
.
startswith
(
'@@'
):
view_name
=
name
[
2
:]
else
:
view_name
=
name
view
=
zapi
.
queryMultiAdapter
((
self
,
request
),
name
=
view_name
)
if
view
is
not
None
:
return
view
factory
=
zapi
.
queryUtility
(
IFactory
,
name
)
if
factory
is
None
:
return
super
(
Adding
,
self
).
publishTraverse
(
request
,
name
)
return
factory
def
action
(
self
,
type_name
=
''
,
id
=
''
):
if
not
type_name
:
raise
UserError
(
_
(
u"You must select the type of object to add."
))
if
type_name
.
startswith
(
'@@'
):
type_name
=
type_name
[
2
:]
if
'/'
in
type_name
:
view_name
=
type_name
.
split
(
'/'
,
1
)[
0
]
else
:
view_name
=
type_name
if
zapi
.
queryMultiAdapter
((
self
,
self
.
request
),
name
=
view_name
)
is
not
None
:
url
=
"%s/%s=%s"
%
(
zapi
.
absoluteURL
(
self
,
self
.
request
),
type_name
,
id
)
self
.
request
.
response
.
redirect
(
url
)
return
if
not
self
.
contentName
:
self
.
contentName
=
id
# TODO: If the factory wrapped by LocationProxy is already a Proxy,
# then ProxyFactory does not do the right thing and the
# original's checker info gets lost. No factory that was
# registered via ZCML and was used via addMenuItem worked
# here. (SR)
factory
=
zapi
.
getUtility
(
IFactory
,
type_name
)
if
not
type
(
factory
)
is
zope
.
security
.
checker
.
Proxy
:
factory
=
LocationProxy
(
factory
,
self
,
type_name
)
factory
=
zope
.
security
.
checker
.
ProxyFactory
(
factory
)
content
=
factory
()
# Can't store security proxies.
# Note that it is important to do this here, rather than
# in add, otherwise, someone might be able to trick add
# into unproxying an existing object,
content
=
removeSecurityProxy
(
content
)
notify
(
ObjectCreatedEvent
(
content
))
self
.
add
(
content
)
self
.
request
.
response
.
redirect
(
self
.
nextURL
())
def
nameAllowed
(
self
):
"""Return whether names can be input by the user."""
return
not
IContainerNamesContainer
.
providedBy
(
self
.
context
)
menu_id
=
None
index
=
ViewPageTemplateFile
(
"add.pt"
)
def
addingInfo
(
self
):
"""Return menu data.
This is sorted by title.
"""
container
=
self
.
context
result
=
[]
for
menu_id
in
(
self
.
menu_id
,
'zope.app.container.add'
):
if
not
menu_id
:
continue
for
item
in
getMenu
(
menu_id
,
self
,
self
.
request
):
extra
=
item
.
get
(
'extra'
)
if
extra
:
factory
=
extra
.
get
(
'factory'
)
if
factory
:
factory
=
zapi
.
getUtility
(
IFactory
,
factory
)
if
not
checkFactory
(
container
,
None
,
factory
):
continue
elif
item
[
'extra'
][
'factory'
]
!=
item
[
'action'
]:
item
[
'has_custom_add_view'
]
=
True
result
.
append
(
item
)
result
.
sort
(
lambda
a
,
b
:
cmp
(
a
[
'title'
],
b
[
'title'
]))
return
result
def
isSingleMenuItem
(
self
):
"Return whether there is single menu item or not."
return
len
(
self
.
addingInfo
())
==
1
def
hasCustomAddView
(
self
):
"This should be called only if there is `singleMenuItem` else return 0"
if
self
.
isSingleMenuItem
():
menu_item
=
self
.
addingInfo
()[
0
]
if
'has_custom_add_view'
in
menu_item
:
return
True
return
False
src/zope/app/container/browser/commontasks.pt
0 → 100644
View file @
160df616
<tal:block define="addingInfo context/@@+/addingInfo|nothing"
condition="addingInfo" i18n:domain="zope">
<tal:block repeat="info addingInfo"
define="namesRequired context/@@+/nameAllowed">
<div tal:define="oddrow repeat/info/odd;
namesRequired context/@@+/nameAllowed;
has_custom_add_view python:'has_custom_add_view' in info"
tal:attributes="class python:oddrow and 'content even' or 'content odd'"
class="even">
<a href="#"
tal:define="baseurl python:request.getURL(1)"
tal:condition="python: not info['action'].startswith('../')
and namesRequired and not has_custom_add_view"
tal:attributes="
href string:${baseurl}/@@contents.html?type_name=${info/action};
class info/selected"
tal:content="info/title" i18n:translate="">Folder
</a>
<a href="#"
tal:define="baseurl python:request.getURL(1)"
tal:condition="python: not info['action'].startswith('../')
and (has_custom_add_view or not namesRequired)"
tal:attributes="
href string:${baseurl}/@@+/action.html?type_name=${info/action};
class info/selected"
tal:content="info/title" i18n:translate="">Folder
</a>
<a href="#"
tal:define="baseurl python:request.getURL(1)"
tal:condition="python: info['action'].startswith('../')"
tal:attributes="
href python: info['action'][3:];
class info/selected"
tal:content="info/title" i18n:translate="">Folder
</a>
</div>
</tal:block>
</tal:block>
src/zope/app/container/browser/configure.zcml
0 → 100644
View file @
160df616
<zope:configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns="http://namespaces.zope.org/browser">
<page
for="zope.app.container.interfaces.IReadContainer"
name="find.html"
permission="zope.ManageContent"
class="zope.app.container.browser.find.Find"
template="find.pt"
menu="zmi_actions" title="Find" />
<page
for="zope.app.container.interfaces.IWriteContainer"
permission="zope.ManageContent"
name="commonTasks"
class="zope.app.container.browser.contents.Contents"
template="commontasks.pt" />
</zope:configure>
src/zope/app/container/browser/contents.pt
0 → 100644
View file @
160df616
<html
metal:use-macro=
"context/@@standard_macros/view"
i18n:domain=
"zope"
>
<body>
<div
metal:fill-slot=
"body"
>
<div
metal:define-macro=
"contents"
>
<form
name=
"containerContentsForm"
method=
"post"
action=
"."
tal:attributes=
"action request/URL"
tal:define=
"container_contents view/listContentInfo"
>
<input
type=
"hidden"
name=
"type_name"
value=
""
tal:attributes=
"value request/type_name"
tal:condition=
"request/type_name|nothing"
/>
<input
type=
"hidden"
name=
"retitle_id"
value=
""
tal:attributes=
"value request/retitle_id"
tal:condition=
"request/retitle_id|nothing"
/>
<div
class=
"page_error"
tal:condition=
"view/error"
tal:content=
"view/error"
i18n:translate=
""
>
Error message
</div>
<table
id=
"sortable"
class=
"listing"
summary=
"Content listing"
i18n:attributes=
"summary"
>
<thead>
<tr>
<th><input
type=
"checkbox"
onchange=
"updateCheckboxes(this, 'slaveBox');"
/></th>
<th
i18n:translate=
""
>
Name
</th>
<th
i18n:translate=
""
>
Title
</th>
<th
i18n:translate=
""
>
Size
</th>
<th
i18n:translate=
""
>
Created
</th>
<th
i18n:translate=
""
>
Modified
</th>
</tr>
</thead>
<tbody>
<metal:block
tal:condition=
"view/hasAdding"
>
<tr
tal:define=
"names_required context/@@+/nameAllowed"
tal:condition=
"python:names_required and request.has_key('type_name')"
>
<td></td>
<td><input
name=
"new_value"
id=
"focusid"
value=
""
/></td>
<td></td>
<td></td>
<td></td>
</tr>
</metal:block>
<metal:block
tal:define=
"supportsRename view/supportsRename"
tal:repeat=
"item container_contents"
>
<tr
tal:define=
"oddrow repeat/item/odd; url item/url;
id_quoted item/id/url:quote"
tal:attributes=
"class python:oddrow and 'even' or 'odd'"
>
<td>
<input
type=
"checkbox"
class=
"noborder slaveBox"
name=
"ids:list"
id=
"#"
value=
"#"
tal:attributes=
"value item/id;
id item/cb_id;
checked request/ids_checked|nothing;"
/>
</td>
<td><a
href=
"#"
tal:attributes=
"href
string:${url}/@@SelectedManagementView.html"
tal:content=
"structure item/icon|default"
>
</a
><span
tal:condition=
"item/rename"
><input
name=
"new_value:list"
tal:attributes=
"value item/id"
/><input
type=
"hidden"
name=
"rename_ids:list"
value=
""
tal:attributes=
"value item/rename"
/></span
><span
tal:condition=
"not:item/rename"
>
<a
href=
"#"
tal:attributes=
"href
string:${url}/@@SelectedManagementView.html"
tal:content=
"item/id"
>
foo
</a
><a
href=
"#"
tal:attributes=
"href
string:${request/URL}?rename_ids:list=${id_quoted}"
tal:condition=
"supportsRename"
>
</a
></span
></td>
<td>
<input
name=
"new_value"
id=
"focusid"
tal:attributes=
"value item/title|nothing"
tal:condition=
"item/retitle"
/>
<a
href=
"#"
tal:attributes=
"href
string:${request/URL}?retitle_id=${id_quoted}"
tal:condition=
"item/retitleable"
tal:content=
"item/title|default"
i18n:translate=
""
>
</a>
<span
tal:condition=
"item/plaintitle"
tal:content=
"item/title|default"
i18n:translate=
""
>
</span>
</td>
<td><span
tal:content=
"item/size/sizeForDisplay|nothing"
i18n:translate=
""
>
</span></td>
<td><span
tal:define=
"created item/created|default"
tal:content=
"created"
i18n:translate=
""
>
</span></td>
<td><span
tal:define=
"modified item/modified|default"
tal:content=
"modified"
i18n:translate=
""
>
</span></td>
</tr>
</metal:block>
</tbody>
</table>
<tal:block
tal:condition=
"view/normalButtons"
>
<input
type=
"submit"
name=
"container_rename_button"
value=
"Rename"
i18n:attributes=
"value container-rename-button"
tal:condition=
"view/supportsRename"
/>
<input
type=
"submit"
name=
"container_cut_button"
value=
"Cut"
i18n:attributes=
"value container-cut-button"
tal:condition=
"view/supportsCut"
/>
<input
type=
"submit"
name=
"container_copy_button"
value=
"Copy"
i18n:attributes=
"value container-copy-button"
tal:condition=
"view/supportsCopy"
/>
<input
type=
"submit"
name=
"container_paste_button"
value=
"Paste"
tal:condition=
"view/hasClipboardContents"
i18n:attributes=
"value container-paste-button"
/>
<input
type=
"submit"
name=
"container_delete_button"
value=
"Delete"
i18n:attributes=
"value container-delete-button"
tal:condition=
"view/supportsDelete"
i18n:domain=
"zope"
/>
<div
tal:condition=
"view/hasAdding"
tal:omit-tag=
""
>
<div
tal:omit-tag=
""
tal:define=
"adding nocall:context/@@+;
addingInfo adding/addingInfo;
has_custom_add_view adding/hasCustomAddView;
names_required adding/nameAllowed"
tal:condition=
"adding/isSingleMenuItem"
>
<input
type=
"submit"
name=
"container_add_button"
value=
"Add"
i18n:attributes=
"value add-button"
i18n:domain=
"zope"
/>
<input
type=
"text"
name=
"single_new_value"
id=
"focusid"
tal:condition=
"python:names_required and not has_custom_add_view"
i18n:domain=
"zope"
/>
<input
type=
"hidden"
name=
"single_type_name"
value=
""
tal:attributes=
"value python:addingInfo[0]['action']"
/>
</div>
</div>
</tal:block>
<div
tal:condition=
"view/specialButtons"
>
<input
type=
"submit"
value=
"Apply"
i18n:attributes=
"value container-apply-button"
/>
<input
type=
"submit"
name=
"container_cancel_button"
value=
"Cancel"
i18n:attributes=
"value container-cancel-button"
/>
</div>
</form>
<script
type=
"text/javascript"
>
<!--
if
(
document
.
containerContentsForm
.
new_value
)
document
.
containerContentsForm
.
new_value
.
focus
();
//-->
</script>
</div>
</div>
</body>
</html>
src/zope/app/container/browser/contents.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""View Class for the Container's Contents view.
$Id$
"""
__docformat__
=
'restructuredtext'
import
urllib
from
zope.exceptions.interfaces
import
UserError
from
zope.security.interfaces
import
Unauthorized
from
zope.security
import
canWrite
from
zope.size.interfaces
import
ISized
from
zope.traversing.interfaces
import
TraversalError
from
zope.publisher.browser
import
BrowserView
from
zope.dublincore.interfaces
import
IZopeDublinCore
from
zope.dublincore.interfaces
import
IDCDescriptiveProperties
from
zope.copypastemove.interfaces
import
IPrincipalClipboard
from
zope.copypastemove.interfaces
import
IObjectCopier
,
IObjectMover
from
zope.copypastemove.interfaces
import
IContainerItemRenamer
from
zope.annotation.interfaces
import
IAnnotations
from
zope.app
import
zapi
from
zope.app.pagetemplate.viewpagetemplatefile
import
ViewPageTemplateFile
from
zope.app.container.i18n
import
ZopeMessageFactory
as
_
from
zope.app.container.browser.adding
import
Adding
from
zope.app.container.interfaces
import
IContainer
,
DuplicateIDError
from
zope.app.container.interfaces
import
IContainerNamesContainer
class
Contents
(
BrowserView
):
__used_for__
=
IContainer
error
=
''
message
=
''
normalButtons
=
False
specialButtons
=
False
supportsRename
=
False
def
listContentInfo
(
self
):
request
=
self
.
request
if
"container_cancel_button"
in
request
:
if
"type_name"
in
request
:
del
request
.
form
[
'type_name'
]
if
"rename_ids"
in
request
and
"new_value"
in
request
:
del
request
.
form
[
'rename_ids'
]
if
"retitle_id"
in
request
and
"new_value"
in
request
:
del
request
.
form
[
'retitle_id'
]
return
self
.
_normalListContentsInfo
()
elif
"container_rename_button"
in
request
and
not
request
.
get
(
"ids"
):
self
.
error
=
_
(
"You didn't specify any ids to rename."
)
elif
"container_add_button"
in
request
:
if
"single_type_name"
in
request
\
and
"single_new_value"
in
request
:
request
.
form
[
'type_name'
]
=
request
[
'single_type_name'
]
request
.
form
[
'new_value'
]
=
request
[
'single_new_value'
]
self
.
addObject
()
elif
'single_type_name'
in
request
\
and
'single_new_value'
not
in
request
:
request
.
form
[
'type_name'
]
=
request
[
'single_type_name'
]
request
.
form
[
'new_value'
]
=
""
self
.
addObject
()
elif
"type_name"
in
request
and
"new_value"
in
request
:
self
.
addObject
()
elif
"rename_ids"
in
request
and
"new_value"
in
request
:
self
.
renameObjects
()
elif
"retitle_id"
in
request
and
"new_value"
in
request
:
self
.
changeTitle
()
elif
"container_cut_button"
in
request
:
self
.
cutObjects
()
elif
"container_copy_button"
in
request
:
self
.
copyObjects
()
elif
"container_paste_button"
in
request
:
self
.
pasteObjects
()
elif
"container_delete_button"
in
request
:
self
.
removeObjects
()
else
:
return
self
.
_normalListContentsInfo
()
if
self
.
error
:
return
self
.
_normalListContentsInfo
()
status
=
request
.
response
.
getStatus
()
if
status
not
in
(
302
,
303
):
# Only redirect if nothing else has
request
.
response
.
redirect
(
request
.
URL
)
return
()
def
normalListContentInfo
(
self
):
return
self
.
_normalListContentsInfo
()
def
_normalListContentsInfo
(
self
):
request
=
self
.
request
self
.
specialButtons
=
(
'type_name'
in
request
or
'rename_ids'
in
request
or
(
'container_rename_button'
in
request
and
request
.
get
(
"ids"
))
or
'retitle_id'
in
request
)
self
.
normalButtons
=
not
self
.
specialButtons
info
=
map
(
self
.
_extractContentInfo
,
self
.
context
.
items
())
self
.
supportsCut
=
info
self
.
supportsCopy
=
info
self
.
supportsDelete
=
info
self
.
supportsPaste
=
self
.
pasteable
()
self
.
supportsRename
=
(
self
.
supportsCut
and
not
IContainerNamesContainer
.
providedBy
(
self
.
context
)
)
return
info
def
_extractContentInfo
(
self
,
item
):
request
=
self
.
request
rename_ids
=
{}
if
"container_rename_button"
in
request
:
for
rename_id
in
request
.
get
(
'ids'
,
()):
rename_ids
[
rename_id
]
=
rename_id
elif
"rename_ids"
in
request
:
for
rename_id
in
request
.
get
(
'rename_ids'
,
()):
rename_ids
[
rename_id
]
=
rename_id
retitle_id
=
request
.
get
(
'retitle_id'
)
id
,
obj
=
item
info
=
{}
info
[
'id'
]
=
info
[
'cb_id'
]
=
id
info
[
'object'
]
=
obj
info
[
'url'
]
=
urllib
.
quote
(
id
.
encode
(
'utf-8'
))
info
[
'rename'
]
=
rename_ids
.
get
(
id
)
info
[
'retitle'
]
=
id
==
retitle_id
zmi_icon
=
zapi
.
queryMultiAdapter
((
obj
,
self
.
request
),
name
=
'zmi_icon'
)
if
zmi_icon
is
None
:
info
[
'icon'
]
=
None
else
:
info
[
'icon'
]
=
zmi_icon
()
dc
=
IZopeDublinCore
(
obj
,
None
)
if
dc
is
not
None
:
info
[
'retitleable'
]
=
canWrite
(
dc
,
'title'
)
info
[
'plaintitle'
]
=
not
info
[
'retitleable'
]
title
=
self
.
safe_getattr
(
dc
,
'title'
,
None
)
if
title
:
info
[
'title'
]
=
title
formatter
=
self
.
request
.
locale
.
dates
.
getFormatter
(
'dateTime'
,
'short'
)
created
=
self
.
safe_getattr
(
dc
,
'created'
,
None
)
if
created
is
not
None
:
info
[
'created'
]
=
formatter
.
format
(
created
)
modified
=
self
.
safe_getattr
(
dc
,
'modified'
,
None
)
if
modified
is
not
None
:
info
[
'modified'
]
=
formatter
.
format
(
modified
)
else
:
info
[
'retitleable'
]
=
0
info
[
'plaintitle'
]
=
1
sized_adapter
=
ISized
(
obj
,
None
)
if
sized_adapter
is
not
None
:
info
[
'size'
]
=
sized_adapter
return
info
def
safe_getattr
(
self
,
obj
,
attr
,
default
):
"""Attempts to read the attr, returning default if Unauthorized."""
try
:
return
getattr
(
obj
,
attr
,
default
)
except
Unauthorized
:
return
default
def
renameObjects
(
self
):
"""Given a sequence of tuples of old, new ids we rename"""
request
=
self
.
request
ids
=
request
.
get
(
"rename_ids"
)
newids
=
request
.
get
(
"new_value"
)
renamer
=
IContainerItemRenamer
(
self
.
context
)
for
oldid
,
newid
in
zip
(
ids
,
newids
):
if
newid
!=
oldid
:
renamer
.
renameItem
(
oldid
,
newid
)
def
changeTitle
(
self
):
"""Given a sequence of tuples of old, new ids we rename"""
request
=
self
.
request
id
=
request
.
get
(
"retitle_id"
)
new
=
request
.
get
(
"new_value"
)
item
=
self
.
context
[
id
]
dc
=
IDCDescriptiveProperties
(
item
)
dc
.
title
=
new
def
hasAdding
(
self
):
"""Returns true if an adding view is available."""
adding
=
zapi
.
queryMultiAdapter
((
self
.
context
,
self
.
request
),
name
=
"+"
)
return
(
adding
is
not
None
)
def
addObject
(
self
):
request
=
self
.
request
if
IContainerNamesContainer
.
providedBy
(
self
.
context
):
new
=
""
else
:
new
=
request
[
"new_value"
]
adding
=
zapi
.
queryMultiAdapter
((
self
.
context
,
self
.
request
),
name
=
"+"
)
if
adding
is
None
:
adding
=
Adding
(
self
.
context
,
request
)
else
:
# Set up context so that the adding can build a url
# if the type name names a view.
# Note that we can't so this for the "adding is None" case
# above, because there is no "+" view.
adding
.
__parent__
=
self
.
context
adding
.
__name__
=
'+'
adding
.
action
(
request
[
'type_name'
],
new
)
def
removeObjects
(
self
):
"""Remove objects specified in a list of object ids"""
request
=
self
.
request
ids
=
request
.
get
(
'ids'
)
if
not
ids
:
self
.
error
=
_
(
"You didn't specify any ids to remove."
)
return
container
=
self
.
context
for
id
in
ids
:
del
container
[
id
]
def
copyObjects
(
self
):
"""Copy objects specified in a list of object ids"""
request
=
self
.
request
ids
=
request
.
get
(
'ids'
)
if
not
ids
:
self
.
error
=
_
(
"You didn't specify any ids to copy."
)
return
container_path
=
zapi
.
getPath
(
self
.
context
)
# For each item, check that it can be copied; if so, save the
# path of the object for later copying when a destination has
# been selected; if not copyable, provide an error message
# explaining that the object can't be copied.
items
=
[]
for
id
in
ids
:
ob
=
self
.
context
[
id
]
copier
=
IObjectCopier
(
ob
)
if
not
copier
.
copyable
():
m
=
{
"name"
:
id
}
title
=
getDCTitle
(
ob
)
if
title
:
m
[
"title"
]
=
title
self
.
error
=
_
(
"Object '${name}' (${title}) cannot be copied"
,
mapping
=
m
)
else
:
self
.
error
=
_
(
"Object '${name}' cannot be copied"
,
mapping
=
m
)
return
items
.
append
(
zapi
.
joinPath
(
container_path
,
id
))
# store the requested operation in the principal annotations:
clipboard
=
getPrincipalClipboard
(
self
.
request
)
clipboard
.
clearContents
()
clipboard
.
addItems
(
'copy'
,
items
)
def
cutObjects
(
self
):
"""move objects specified in a list of object ids"""
request
=
self
.
request
ids
=
request
.
get
(
'ids'
)
if
not
ids
:
self
.
error
=
_
(
"You didn't specify any ids to cut."
)
return
container_path
=
zapi
.
getPath
(
self
.
context
)
# For each item, check that it can be moved; if so, save the
# path of the object for later moving when a destination has
# been selected; if not movable, provide an error message
# explaining that the object can't be moved.
items
=
[]
for
id
in
ids
:
ob
=
self
.
context
[
id
]
mover
=
IObjectMover
(
ob
)
if
not
mover
.
moveable
():
m
=
{
"name"
:
id
}
title
=
getDCTitle
(
ob
)
if
title
:
m
[
"title"
]
=
title
self
.
error
=
_
(
"Object '${name}' (${title}) cannot be moved"
,
mapping
=
m
)
else
:
self
.
error
=
_
(
"Object '${name}' cannot be moved"
,
mapping
=
m
)
return
items
.
append
(
zapi
.
joinPath
(
container_path
,
id
))
# store the requested operation in the principal annotations:
clipboard
=
getPrincipalClipboard
(
self
.
request
)
clipboard
.
clearContents
()
clipboard
.
addItems
(
'cut'
,
items
)
def
pasteable
(
self
):
"""Decide if there is anything to paste
"""
target
=
self
.
context
clipboard
=
getPrincipalClipboard
(
self
.
request
)
items
=
clipboard
.
getContents
()
for
item
in
items
:
try
:
obj
=
zapi
.
traverse
(
target
,
item
[
'target'
])
except
TraversalError
:
pass
else
:
if
item
[
'action'
]
==
'cut'
:
mover
=
IObjectMover
(
obj
)
moveableTo
=
self
.
safe_getattr
(
mover
,
'moveableTo'
,
None
)
if
moveableTo
is
None
or
not
moveableTo
(
target
):
return
False
elif
item
[
'action'
]
==
'copy'
:
copier
=
IObjectCopier
(
obj
)
copyableTo
=
self
.
safe_getattr
(
copier
,
'copyableTo'
,
None
)
if
copyableTo
is
None
or
not
copyableTo
(
target
):
return
False
else
:
raise
return
True
def
pasteObjects
(
self
):
"""Paste ojects in the user clipboard to the container
"""
target
=
self
.
context
clipboard
=
getPrincipalClipboard
(
self
.
request
)
items
=
clipboard
.
getContents
()
moved
=
False
not_pasteable_ids
=
[]
for
item
in
items
:
duplicated_id
=
False
try
:
obj
=
zapi
.
traverse
(
target
,
item
[
'target'
])
except
TraversalError
:
pass
else
:
if
item
[
'action'
]
==
'cut'
:
mover
=
IObjectMover
(
obj
)
try
:
mover
.
moveTo
(
target
)
moved
=
True
except
DuplicateIDError
:
duplicated_id
=
True
elif
item
[
'action'
]
==
'copy'
:
copier
=
IObjectCopier
(
obj
)
try
:
copier
.
copyTo
(
target
)
except
DuplicateIDError
:
duplicated_id
=
True
else
:
raise
if
duplicated_id
:
not_pasteable_ids
.
append
(
zapi
.
getName
(
obj
))
if
moved
:
# Clear the clipboard if we do a move, but not if we only do a copy
clipboard
.
clearContents
()
if
not_pasteable_ids
!=
[]:
# Show the ids of objects that can't be pasted because
# their ids are already taken.
# TODO Can't we add a 'copy_of' or something as a prefix
# instead of raising an exception ?
raise
UserError
(
_
(
"The given name(s) %s is / are already being used"
%
(
str
(
not_pasteable_ids
))))
def
hasClipboardContents
(
self
):
"""Interogate the ``PrinicipalAnnotation`` to see if clipboard
contents exist."""
if
not
self
.
supportsPaste
:
return
False
# touch at least one item to in clipboard confirm contents
clipboard
=
getPrincipalClipboard
(
self
.
request
)
items
=
clipboard
.
getContents
()
for
item
in
items
:
try
:
zapi
.
traverse
(
self
.
context
,
item
[
'target'
])
except
TraversalError
:
pass
else
:
return
True
return
False
contents
=
ViewPageTemplateFile
(
'contents.pt'
)
contentsMacros
=
contents
_index
=
ViewPageTemplateFile
(
'index.pt'
)
def
index
(
self
):
if
'index.html'
in
self
.
context
:
self
.
request
.
response
.
redirect
(
'index.html'
)
return
''
return
self
.
_index
()
class
JustContents
(
Contents
):
"""Like Contents, but does't delegate to item named index.html"""
def
index
(
self
):
return
self
.
_index
()
def
getDCTitle
(
ob
):
dc
=
IDCDescriptiveProperties
(
ob
,
None
)
if
dc
is
None
:
return
None
else
:
return
dc
.
title
def
getPrincipalClipboard
(
request
):
"""Return the clipboard based on the request."""
user
=
request
.
principal
annotations
=
IAnnotations
(
user
)
return
IPrincipalClipboard
(
annotations
)
src/zope/app/container/browser/find.pt
0 → 100644
View file @
160df616
<html
metal:use-macro=
"context/@@standard_macros/dialog"
i18n:domain=
"zope"
>
<body>
<div
metal:fill-slot=
"body"
>
<form
action=
"@@find.html"
method=
"get"
>
<input
type=
"text"
name=
"ids"
value=
""
/><br
/>
<input
type=
"submit"
name=
"find_submit"
value=
" Find "
i18n:attributes=
"value find-button"
/>
</form>
<table
tal:condition=
"request/ids|nothing"
>
<tr
tal:repeat=
"item python:view.findByIds(request['ids'])"
>
<td>
<a
href=
""
tal:attributes=
"href item/url"
tal:content=
"item/id"
>
id
</a>
</td>
</tr>
</table>
</div>
</body>
</html>
src/zope/app/container/browser/find.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Find View Class
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.traversing.api
import
getName
from
zope.traversing.browser.absoluteurl
import
absoluteURL
from
zope.publisher.browser
import
BrowserView
from
zope.app.container.find
import
SimpleIdFindFilter
from
zope.app.container.interfaces
import
IFind
# Very simple implementation right now
class
Find
(
BrowserView
):
def
findByIds
(
self
,
ids
):
"""Do a find for the `ids` listed in `ids`, which is a string."""
finder
=
IFind
(
self
.
context
)
ids
=
ids
.
split
()
# if we don't have any ids listed, don't search at all
if
not
ids
:
return
[]
request
=
self
.
request
result
=
[]
for
object
in
finder
.
find
([
SimpleIdFindFilter
(
ids
)]):
url
=
absoluteURL
(
object
,
request
)
result
.
append
({
'id'
:
getName
(
object
),
'url'
:
url
})
return
result
src/zope/app/container/browser/ftests/__init__.py
0 → 100644
View file @
160df616
#
# This file is necessary to make this directory a package.
src/zope/app/container/browser/ftests/configure.zcml
0 → 100644
View file @
160df616
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<class class=".test_contents.ReadOnlyContainer">
<require
permission="zope.ManageContent"
interface="zope.app.container.interfaces.IReadContainer"
/>
</class>
<browser:containerViews
for="zope.app.container.interfaces.IReadContainer"
contents="zope.ManageContent" />
</configure>
src/zope/app/container/browser/ftests/index.txt
0 → 100644
View file @
160df616
The containerViews directive lets us associate some standard forms
for containers with an interface. There's an "index.html" view that
provides a listing of the contained objects without provinding any way
to manage them (though it allows us to visit them by clicking on
links).
We can get this view from the root folder easily::
>>> response = http(r"""
... GET / HTTP/1.1
... """)
And we can check that there isn't a form (where the management
operations would have buttons)::
>>> body = response.getBody().lower()
>>> "<form" in body
False
src/zope/app/container/browser/ftests/test_contents.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Functional tests for the Container's 'Contents' view
$Id$
"""
import
unittest
from
persistent
import
Persistent
import
transaction
from
zope
import
copypastemove
from
zope.interface
import
implements
,
Interface
from
zope.annotation.interfaces
import
IAttributeAnnotatable
from
zope.dublincore.interfaces
import
IZopeDublinCore
from
zope.app.container.interfaces
import
IReadContainer
,
IContained
from
zope.app.testing
import
ztapi
from
zope.app.testing.functional
import
BrowserTestCase
from
zope.app.testing.functional
import
FunctionalDocFileSuite
from
zope.app.container.testing
import
AppContainerLayer
class
IImmovable
(
Interface
):
"""Marker interface for immovable objects."""
class
IUncopyable
(
Interface
):
"""Marker interface for uncopyable objects."""
class
File
(
Persistent
):
implements
(
IAttributeAnnotatable
)
class
ImmovableFile
(
File
):
implements
(
IImmovable
)
class
UncopyableFile
(
File
):
implements
(
IUncopyable
)
class
ObjectNonCopier
(
copypastemove
.
ObjectCopier
):
def
copyable
(
self
):
return
False
class
ObjectNonMover
(
copypastemove
.
ObjectMover
):
def
moveable
(
self
):
return
False
class
ReadOnlyContainer
(
Persistent
):
implements
(
IReadContainer
,
IContained
)
__parent__
=
__name__
=
None
def
__init__
(
self
):
self
.
data
=
{}
def
keys
(
self
):
return
self
.
data
.
keys
()
def
__getitem__
(
self
,
key
):
return
self
.
data
[
key
]
def
get
(
self
,
key
,
default
=
None
):
return
self
.
data
.
get
(
key
,
default
)
def
__iter__
(
self
):
return
iter
(
self
.
data
)
def
values
(
self
):
return
self
.
data
.
values
()
def
__len__
(
self
):
return
len
(
self
.
data
)
def
items
(
self
):
return
self
.
data
.
items
()
def
__contains__
(
self
,
key
):
return
key
in
self
.
data
def
has_key
(
self
,
key
):
return
self
.
data
.
has_key
(
key
)
class
Test
(
BrowserTestCase
):
def
test_inplace_add
(
self
):
root
=
self
.
getRootFolder
()
self
.
assert_
(
'foo'
not
in
root
)
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'type_name'
:
u'zope.app.content.File'
})
body
=
' '
.
join
(
response
.
getBody
().
split
())
self
.
assert_
(
body
.
find
(
'type="hidden" name="type_name"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'input name="new_value"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_cancel_button"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_rename_button"'
)
<
0
)
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'type_name'
:
u'zope.app.content.File'
,
'new_value'
:
'foo'
})
self
.
assertEqual
(
response
.
getStatus
(),
302
)
self
.
assertEqual
(
response
.
getHeader
(
'Location'
),
'http://localhost/@@contents.html'
)
root
.
_p_jar
.
sync
()
self
.
assert_
(
'foo'
in
root
)
def
test_inplace_rename_multiple
(
self
):
root
=
self
.
getRootFolder
()
root
[
'foo'
]
=
File
()
self
.
assert_
(
'foo'
in
root
)
transaction
.
commit
()
# Check that we don't change mode if there are no items selected
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'container_rename_button'
:
u''
})
body
=
' '
.
join
(
response
.
getBody
().
split
())
self
.
assert_
(
body
.
find
(
'input name="new_value:list"'
)
<
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_cancel_button"'
)
<
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_rename_button"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'div class="page_error"'
)
>=
0
)
# Check normal multiple select
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'container_rename_button'
:
u''
,
'ids'
:
[
'foo'
]})
body
=
' '
.
join
(
response
.
getBody
().
split
())
self
.
assert_
(
body
.
find
(
'input name="new_value:list"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_cancel_button"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_rename_button"'
)
<
0
)
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'rename_ids'
:
[
'foo'
],
'new_value'
:
[
'bar'
]})
self
.
assertEqual
(
response
.
getStatus
(),
302
)
self
.
assertEqual
(
response
.
getHeader
(
'Location'
),
'http://localhost/@@contents.html'
)
root
.
_p_jar
.
sync
()
self
.
assert_
(
'foo'
not
in
root
)
self
.
assert_
(
'bar'
in
root
)
def
test_inplace_rename_single
(
self
):
root
=
self
.
getRootFolder
()
root
[
'foo'
]
=
File
()
self
.
assert_
(
'foo'
in
root
)
transaction
.
commit
()
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'rename_ids'
:
[
'foo'
]})
body
=
' '
.
join
(
response
.
getBody
().
split
())
self
.
assert_
(
body
.
find
(
'input name="new_value:list"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_cancel_button"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_rename_button"'
)
<
0
)
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'rename_ids'
:
[
'foo'
],
'new_value'
:
[
'bar'
]})
self
.
assertEqual
(
response
.
getStatus
(),
302
)
self
.
assertEqual
(
response
.
getHeader
(
'Location'
),
'http://localhost/@@contents.html'
)
root
.
_p_jar
.
sync
()
self
.
assert_
(
'foo'
not
in
root
)
self
.
assert_
(
'bar'
in
root
)
def
test_inplace_change_title
(
self
):
root
=
self
.
getRootFolder
()
root
[
'foo'
]
=
File
()
transaction
.
commit
()
self
.
assert_
(
'foo'
in
root
)
dc
=
IZopeDublinCore
(
root
[
'foo'
])
self
.
assert_
(
dc
.
title
==
''
)
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'retitle_id'
:
u'foo'
})
body
=
' '
.
join
(
response
.
getBody
().
split
())
self
.
assert_
(
body
.
find
(
'type="hidden" name="retitle_id"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'input name="new_value"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_cancel_button"'
)
>=
0
)
self
.
assert_
(
body
.
find
(
'type="submit" name="container_rename_button"'
)
<
0
)
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'retitle_id'
:
u'foo'
,
'new_value'
:
u'test title'
})
self
.
assertEqual
(
response
.
getStatus
(),
302
)
self
.
assertEqual
(
response
.
getHeader
(
'Location'
),
'http://localhost/@@contents.html'
)
root
.
_p_jar
.
sync
()
self
.
assert_
(
'foo'
in
root
)
dc
=
IZopeDublinCore
(
root
[
'foo'
])
self
.
assert_
(
dc
.
title
==
'test title'
)
def
test_pasteable_for_deleted_clipboard_item
(
self
):
"""Tests Paste button visibility when copied item is deleted."""
root
=
self
.
getRootFolder
()
root
[
'foo'
]
=
File
()
# item to be copied/deleted
root
[
'bar'
]
=
File
()
# ensures that there's always an item in
# the collection view
transaction
.
commit
()
# confirm foo in contents, Copy button visible, Paste not visible
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
)
self
.
assertEqual
(
response
.
getStatus
(),
200
)
self
.
assert_
(
response
.
getBody
().
find
(
'<a href="foo/@@SelectedManagementView.html">foo</a>'
)
!=
-
1
)
self
.
assert_
(
response
.
getBody
().
find
(
'<input type="submit" name="container_copy_button"'
)
!=
-
1
)
self
.
assert_
(
response
.
getBody
().
find
(
'<input type="submit" name="container_paste_button"'
)
==
-
1
)
# copy foo - confirm Paste visible
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'ids'
:
(
'foo'
,),
'container_copy_button'
:
''
})
self
.
assertEqual
(
response
.
getStatus
(),
302
)
self
.
assertEqual
(
response
.
getHeader
(
'Location'
),
'http://localhost/@@contents.html'
)
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
)
self
.
assertEqual
(
response
.
getStatus
(),
200
)
self
.
assert_
(
response
.
getBody
().
find
(
'<input type="submit" name="container_paste_button"'
)
!=
-
1
)
# delete foo -> nothing valid to paste -> Paste should not be visible
del
root
[
'foo'
]
transaction
.
commit
()
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
)
self
.
assertEqual
(
response
.
getStatus
(),
200
)
self
.
assert_
(
response
.
getBody
().
find
(
'<input type="submit" name="container_paste_button"'
)
==
-
1
)
def
test_paste_for_deleted_clipboard_item
(
self
):
"""Tests paste operation when one of two copied items is deleted."""
root
=
self
.
getRootFolder
()
root
[
'foo'
]
=
File
()
root
[
'bar'
]
=
File
()
transaction
.
commit
()
# confirm foo/bar in contents, Copy button visible, Paste not visible
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
)
self
.
assertEqual
(
response
.
getStatus
(),
200
)
self
.
assert_
(
response
.
getBody
().
find
(
'<a href="foo/@@SelectedManagementView.html">foo</a>'
)
!=
-
1
)
self
.
assert_
(
response
.
getBody
().
find
(
'<a href="bar/@@SelectedManagementView.html">bar</a>'
)
!=
-
1
)
self
.
assert_
(
response
.
getBody
().
find
(
'<input type="submit" name="container_copy_button"'
)
!=
-
1
)
self
.
assert_
(
response
.
getBody
().
find
(
'<input type="submit" name="container_paste_button"'
)
==
-
1
)
# copy foo and bar - confirm Paste visible
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'ids'
:
(
'foo'
,
'bar'
),
'container_copy_button'
:
''
})
self
.
assertEqual
(
response
.
getStatus
(),
302
)
self
.
assertEqual
(
response
.
getHeader
(
'Location'
),
'http://localhost/@@contents.html'
)
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
)
self
.
assertEqual
(
response
.
getStatus
(),
200
)
self
.
assert_
(
response
.
getBody
().
find
(
'<input type="submit" name="container_paste_button"'
)
!=
-
1
)
# delete only foo -> bar still available -> Paste should be visible
del
root
[
'foo'
]
transaction
.
commit
()
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
)
self
.
assertEqual
(
response
.
getStatus
(),
200
)
self
.
assert_
(
response
.
getBody
().
find
(
'<input type="submit" name="container_paste_button"'
)
!=
-
1
)
# paste clipboard contents - only bar should be copied
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'container_paste_button'
:
''
})
self
.
assertEqual
(
response
.
getStatus
(),
302
)
self
.
assertEqual
(
response
.
getHeader
(
'Location'
),
'http://localhost/@@contents.html'
)
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
)
self
.
assertEqual
(
response
.
getStatus
(),
200
)
root
.
_p_jar
.
sync
()
self
.
assertEqual
(
tuple
(
root
.
keys
()),
(
'bar'
,
'bar-2'
))
def
test_readonly_display
(
self
):
root
=
self
.
getRootFolder
()
root
[
'foo'
]
=
ReadOnlyContainer
()
transaction
.
commit
()
response
=
self
.
publish
(
'/foo/@@contents.html'
,
basic
=
'mgr:mgrpw'
)
self
.
assertEqual
(
response
.
getStatus
(),
200
)
def
test_uncopyable_object
(
self
):
ztapi
.
provideAdapter
(
IUncopyable
,
copypastemove
.
interfaces
.
IObjectCopier
,
ObjectNonCopier
)
root
=
self
.
getRootFolder
()
root
[
'uncopyable'
]
=
UncopyableFile
()
transaction
.
commit
()
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'ids'
:
[
u'uncopyable'
],
'container_copy_button'
:
u'Copy'
})
self
.
assertEqual
(
response
.
getStatus
(),
200
)
body
=
response
.
getBody
()
self
.
assert_
(
"cannot be copied"
in
body
)
def
test_unmoveable_object
(
self
):
ztapi
.
provideAdapter
(
IImmovable
,
copypastemove
.
interfaces
.
IObjectMover
,
ObjectNonMover
)
root
=
self
.
getRootFolder
()
root
[
'immovable'
]
=
ImmovableFile
()
transaction
.
commit
()
response
=
self
.
publish
(
'/@@contents.html'
,
basic
=
'mgr:mgrpw'
,
form
=
{
'ids'
:
[
u'immovable'
],
'container_cut_button'
:
u'Cut'
})
self
.
assertEqual
(
response
.
getStatus
(),
200
)
body
=
response
.
getBody
()
self
.
assert_
(
"cannot be moved"
in
body
)
def
test_suite
():
suite
=
unittest
.
TestSuite
()
Test
.
layer
=
AppContainerLayer
suite
.
addTest
(
unittest
.
makeSuite
(
Test
))
index
=
FunctionalDocFileSuite
(
"index.txt"
)
index
.
layer
=
AppContainerLayer
suite
.
addTest
(
index
)
return
suite
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/browser/index.pt
0 → 100644
View file @
160df616
<html
metal:use-macro=
"context/@@standard_macros/page"
i18n:domain=
"zope"
>
<head>
<style
metal:fill-slot=
"headers"
type=
"text/css"
>
<!
--
.ContentIcon
{
width
:
20px
;
}
.ContentTitle
{
text-align
:
left
;
}
--
>
</style>
</head>
<body>
<div
metal:fill-slot=
"body"
>
<table
id=
"sortable"
class=
"listing"
summary=
"Content listing"
cellpadding=
"2"
cellspacing=
"0"
i18n:attributes=
"summary"
>
<thead>
<tr>
<th>
</th>
<th
i18n:translate=
""
>
Name
</th>
<th
i18n:translate=
""
>
Title
</th>
<th
i18n:translate=
""
>
Created
</th>
<th
i18n:translate=
""
>
Modified
</th>
</tr>
</thead>
<tbody>
<tr
tal:repeat=
"info view/listContentInfo"
>
<td>
<a
href=
"#"
tal:attributes=
"href info/url"
tal:content=
"structure info/icon|default"
/>
</td>
<td
class=
"ContentTitle"
>
<a
href=
"subfolder_id"
tal:attributes=
"href info/url"
tal:content=
"info/id"
i18n:translate=
""
>
ID here
</a>
</td>
<td><span
tal:content=
"info/title|default"
i18n:translate=
""
>
</span></td>
<td><span
tal:content=
"info/created|default"
i18n:translate=
""
>
</span></td>
<td><span
tal:content=
"info/modified|default"
i18n:translate=
""
>
</span></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
src/zope/app/container/browser/meta.zcml
0 → 100644
View file @
160df616
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta">
<meta:directives namespace="http://namespaces.zope.org/browser">
<!-- browser cotainer views -->
<meta:directive
name="containerViews"
schema=".metaconfigure.IContainerViews"
handler=".metaconfigure.containerViews"
/>
</meta:directives>
</configure>
\ No newline at end of file
src/zope/app/container/browser/metaconfigure.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Container-specific browser ZCML namespace directive handlers
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.interface
import
Interface
from
zope.configuration.fields
import
GlobalObject
from
zope.publisher.interfaces.browser
import
IDefaultBrowserLayer
from
zope.schema
import
Id
from
zope.security.zcml
import
Permission
from
zope.app.publisher.browser.viewmeta
import
page
,
view
from
zope.app.container.browser.contents
import
Contents
from
zope.app.container.browser.adding
import
Adding
from
zope.app.component.back35
import
LayerField
from
zope.app.container.i18n
import
ZopeMessageFactory
as
_
class
IContainerViews
(
Interface
):
"""Define several container views for an `IContainer` implementation."""
for_
=
GlobalObject
(
title
=
u"The declaration this containerViews are for."
,
description
=
u"""
The containerViews will be available for all objects that
provide this declaration.
"""
,
required
=
True
)
contents
=
Permission
(
title
=
u"The permission needed for content page."
,
required
=
False
)
index
=
Permission
(
title
=
u"The permission needed for index page."
,
required
=
False
)
add
=
Permission
(
title
=
u"The permission needed for add page."
,
required
=
False
)
layer
=
LayerField
(
title
=
_
(
"The layer the view is in."
),
description
=
_
(
"""A skin is composed of layers. It is common to put
skin specific views in a layer named after the skin. If the 'layer'
attribute is not supplied, it defaults to 'default'."""
),
required
=
False
)
def
containerViews
(
_context
,
for_
,
contents
=
None
,
add
=
None
,
index
=
None
,
layer
=
IDefaultBrowserLayer
):
"""Set up container views for a given content type."""
if
for_
is
None
:
raise
ValueError
(
"A for interface must be specified."
)
if
contents
is
not
None
:
from
zope.app.menus
import
zmi_views
page
(
_context
,
name
=
'contents.html'
,
permission
=
contents
,
for_
=
for_
,
layer
=
layer
,
class_
=
Contents
,
attribute
=
'contents'
,
menu
=
zmi_views
,
title
=
_
(
'Contents'
))
if
index
is
not
None
:
page
(
_context
,
name
=
'index.html'
,
permission
=
index
,
for_
=
for_
,
layer
=
layer
,
class_
=
Contents
,
attribute
=
'index'
)
if
add
is
not
None
:
from
zope.app.menus
import
zmi_actions
viewObj
=
view
(
_context
,
name
=
'+'
,
layer
=
layer
,
menu
=
zmi_actions
,
title
=
_
(
'Add'
),
for_
=
for_
,
permission
=
add
,
class_
=
Adding
)
viewObj
.
page
(
_context
,
name
=
'index.html'
,
attribute
=
'index'
)
viewObj
.
page
(
_context
,
name
=
'action.html'
,
attribute
=
'action'
)
viewObj
()
src/zope/app/container/browser/tests/__init__.py
0 → 100644
View file @
160df616
#
# This file is necessary to make this directory a package.
src/zope/app/container/browser/tests/test_adding.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
"""Adding implementation tests
$Id$
"""
import
unittest
import
zope.interface
import
zope.security.checker
from
zope.component.interfaces
import
IFactory
from
zope.component.interfaces
import
ComponentLookupError
from
zope.interface
import
implements
,
Interface
,
directlyProvides
from
zope.publisher.browser
import
TestRequest
from
zope.publisher.interfaces.browser
import
IBrowserRequest
from
zope.publisher.browser
import
BrowserView
from
zope.security.interfaces
import
ForbiddenAttribute
from
zope.testing.doctestunit
import
DocTestSuite
from
zope.exceptions.interfaces
import
UserError
from
zope.traversing.browser
import
AbsoluteURL
from
zope.traversing.browser.interfaces
import
IAbsoluteURL
from
zope.traversing.interfaces
import
IContainmentRoot
from
zope.app
import
zapi
from
zope.app.testing
import
ztapi
from
zope.app.testing.placelesssetup
import
PlacelessSetup
,
setUp
,
tearDown
from
zope.app.publisher.interfaces.browser
import
AddMenu
from
zope.app.publisher.interfaces.browser
import
IMenuItemType
,
IBrowserMenu
from
zope.app.publisher.browser.menu
import
BrowserMenuItem
,
BrowserMenu
from
zope.app.container.interfaces
import
IAdding
from
zope.app.container.interfaces
import
IObjectAddedEvent
from
zope.app.container.interfaces
import
IContainerNamesContainer
from
zope.app.container.interfaces
import
INameChooser
from
zope.app.container.interfaces
import
IContainer
from
zope.app.container.contained
import
contained
from
zope.app.container.browser.adding
import
Adding
from
zope.app.container.sample
import
SampleContainer
class
Root
(
object
):
implements
(
IContainmentRoot
)
class
Container
(
SampleContainer
):
pass
class
CreationView
(
BrowserView
):
def
action
(
self
):
return
'been there, done that'
class
Content
(
object
):
pass
class
Factory
(
object
):
implements
(
IFactory
)
title
=
''
description
=
''
def
getInterfaces
(
self
):
return
()
def
__call__
(
self
):
return
Content
()
class
AbsoluteURL
(
BrowserView
):
def
__str__
(
self
):
if
IContainmentRoot
.
providedBy
(
self
.
context
):
return
''
name
=
self
.
context
.
__name__
url
=
zapi
.
absoluteURL
(
zapi
.
getParent
(
self
.
context
),
self
.
request
)
url
+=
'/'
+
name
return
url
__call__
=
__str__
def
defineMenuItem
(
menuItemType
,
for_
,
action
,
title
=
u''
,
extra
=
None
):
newclass
=
type
(
title
,
(
BrowserMenuItem
,),
{
'title'
:
title
,
'action'
:
action
,
'_for'
:
for_
,
'extra'
:
extra
})
zope
.
interface
.
classImplements
(
newclass
,
menuItemType
)
ztapi
.
provideAdapter
((
for_
,
IBrowserRequest
),
menuItemType
,
newclass
,
title
)
def
registerAddMenu
():
ztapi
.
provideUtility
(
IMenuItemType
,
AddMenu
,
'zope.app.container.add'
)
ztapi
.
provideUtility
(
IBrowserMenu
,
BrowserMenu
(
'zope.app.container.add'
,
u''
,
u''
),
'zope.app.container.add'
)
class
Test
(
PlacelessSetup
,
unittest
.
TestCase
):
def
setUp
(
self
):
super
(
Test
,
self
).
setUp
()
def
test
(
self
):
container
=
Container
()
request
=
TestRequest
()
adding
=
Adding
(
container
,
request
)
ztapi
.
browserView
(
IAdding
,
"Thing"
,
CreationView
)
self
.
assertEqual
(
adding
.
contentName
,
None
)
view
=
adding
.
publishTraverse
(
request
,
'Thing=foo'
)
self
.
assertEqual
(
view
.
action
(),
'been there, done that'
)
self
.
assertEqual
(
adding
.
contentName
,
'foo'
)
o
=
object
()
result
=
adding
.
add
(
o
)
# Check the state of the container and result
self
.
assertEqual
(
container
[
"foo"
],
o
)
self
.
assertEqual
(
result
,
o
)
def
testNoNameGiven
(
self
):
container
=
Container
()
request
=
TestRequest
()
adding
=
Adding
(
container
,
request
)
ztapi
.
browserView
(
IAdding
,
"Thing"
,
CreationView
)
self
.
assertEqual
(
adding
.
contentName
,
None
)
view
=
adding
.
publishTraverse
(
request
,
'Thing='
)
self
.
assertEqual
(
adding
.
contentName
,
''
)
def
testAction
(
self
):
# make a private factory
ztapi
.
provideUtility
(
IFactory
,
Factory
(),
'fooprivate'
)
factory
=
Factory
()
factory
.
__Security_checker__
=
zope
.
security
.
checker
.
NamesChecker
(
[
'__call__'
])
ztapi
.
provideUtility
(
IFactory
,
factory
,
'foo'
)
container
=
Container
()
adding
=
Adding
(
container
,
TestRequest
())
adding
.
nextURL
=
lambda
:
'.'
adding
.
nameAllowed
=
lambda
:
True
# we can't use a private factory:
self
.
assertRaises
(
ForbiddenAttribute
,
adding
.
action
,
type_name
=
'fooprivate'
,
id
=
'bar'
)
# typical add - id is provided by user
adding
.
action
(
type_name
=
'foo'
,
id
=
'bar'
)
self
.
assert_
(
'bar'
in
container
)
# missing type_name
self
.
assertRaises
(
UserError
,
adding
.
action
,
id
=
'bar'
)
# missing id
self
.
assertRaises
(
UserError
,
adding
.
action
,
type_name
=
'foo'
)
# bad type_name
self
.
assertRaises
(
ComponentLookupError
,
adding
.
action
,
type_name
=
'***'
,
id
=
'bar'
)
# alternative add - id is provided internally instead of from user
adding
.
nameAllowed
=
lambda
:
False
adding
.
contentName
=
'baz'
adding
.
action
(
type_name
=
'foo'
)
self
.
assert_
(
'baz'
in
container
)
# alternative add w/missing contentName
# Note: Passing is None as object name might be okay, if the container
# is able to hand out ids itself. Let's not require a content
# name to be specified!
# For the container, (or really, the chooser, to choose, we have to
# marke the container as a ContainerNamesContainer
directlyProvides
(
container
,
IContainerNamesContainer
)
adding
.
contentName
=
None
adding
.
action
(
type_name
=
'foo'
)
self
.
assert_
(
'Content'
in
container
)
def
test_action
(
self
):
container
=
Container
()
container
=
contained
(
container
,
Root
(),
"container"
)
request
=
TestRequest
()
adding
=
Adding
(
container
,
request
)
adding
.
__name__
=
'+'
ztapi
.
browserView
(
IAdding
,
"Thing"
,
CreationView
)
ztapi
.
browserView
(
Interface
,
"absolute_url"
,
AbsoluteURL
)
ztapi
.
browserView
(
None
,
''
,
AbsoluteURL
,
providing
=
IAbsoluteURL
)
self
.
assertRaises
(
UserError
,
adding
.
action
,
''
,
'foo'
)
adding
.
action
(
'Thing'
,
'foo'
)
self
.
assertEqual
(
adding
.
request
.
response
.
getHeader
(
'location'
),
'/container/+/Thing=foo'
)
adding
.
action
(
'Thing/screen1'
,
'foo'
)
self
.
assertEqual
(
adding
.
request
.
response
.
getHeader
(
'location'
),
'/container/+/Thing/screen1=foo'
)
def
test_publishTraverse_factory
(
self
):
factory
=
Factory
()
ztapi
.
provideUtility
(
IFactory
,
factory
,
'foo'
)
container
=
Container
()
request
=
TestRequest
()
adding
=
Adding
(
container
,
request
)
self
.
assert_
(
adding
.
publishTraverse
(
request
,
'foo'
)
is
factory
)
def
test_constraint_driven_addingInfo
():
"""
>>> registerAddMenu()
>>> class TestMenu(zope.interface.Interface):
... pass
>>> zope.interface.directlyProvides(TestMenu, IMenuItemType)
>>> ztapi.provideUtility(IMenuItemType, TestMenu, 'TestMenu')
>>> ztapi.provideUtility(IBrowserMenu, BrowserMenu('TestMenu', u'', u''),
... 'TestMenu')
>>> defineMenuItem(TestMenu, IAdding, '', 'item1')
>>> defineMenuItem(TestMenu, IAdding, '', 'item2')
>>> defineMenuItem(AddMenu, IAdding, '', 'item3', extra={'factory': 'f1'})
>>> defineMenuItem(AddMenu, IAdding, '', 'item4', extra={'factory': 'f2'})
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... if not isinstance(object, F1):
... raise zope.interface.Invalid()
>>> def prefactory(container, name, factory):
... if factory._callable is not F1:
... raise zope.interface.Invalid()
>>> pre.factory = prefactory
>>> class IContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(object):
... zope.interface.implements(IContainer)
>>> from zope.component.factory import Factory
>>> ztapi.provideUtility(IFactory, Factory(F1), 'f1')
>>> ztapi.provideUtility(IFactory, Factory(F2), 'f2')
>>> from zope.app.container.browser.adding import Adding
>>> adding = Adding(Container(), TestRequest())
>>> items = adding.addingInfo()
>>> len(items)
1
>>> items[0]['title']
'item3'
>>> adding.menu_id = 'TestMenu'
>>> items = adding.addingInfo()
>>> len(items)
3
>>> items[0]['title']
'item1'
>>> items[1]['title']
'item2'
>>> items[2]['title']
'item3'
"""
def
test_constraint_driven_add
():
"""
>>> from zope.app.container.sample import SampleContainer
>>> from zope.app.container.browser.adding import Adding
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... "a mock item constraint "
... if not isinstance(object, F1):
... raise zope.interface.Invalid('not a valid child')
>>> class ITestContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(SampleContainer):
... zope.interface.implements(ITestContainer)
>>> adding = Adding(Container(), TestRequest())
>>> c = adding.add(F1())
This test should fail, because the container only
accepts instances of F1
>>> adding.add(F2())
Traceback (most recent call last):
...
Invalid: not a valid child
>>> class ValidContainer(SampleContainer):
... zope.interface.implements(ITestContainer)
>>> def constr(container):
... "a mock container constraint"
... if not isinstance(container, ValidContainer):
... raise zope.interface.Invalid('not a valid container')
... return True
>>> class I2(zope.interface.Interface):
... __parent__ = zope.schema.Field(constraint = constr)
>>> zope.interface.classImplements(F1, I2)
This adding now fails, because the Container is not a valid
parent for F1
>>> c = adding.add(F1())
Traceback (most recent call last):
...
Invalid: not a valid container
>>> adding = Adding(ValidContainer(), TestRequest())
>>> c = adding.add(F1())
"""
def
test_nameAllowed
():
"""
Test for nameAllowed in adding.py
>>> from zope.app.container.browser.adding import Adding
>>> from zope.app.container.interfaces import IContainerNamesContainer
Class implements IContainerNamesContainer
>>> class FakeContainer(object):
... zope.interface.implements(IContainerNamesContainer)
nameAllowed returns False if the class imlements
IContainerNamesContainer
>>> adding = Adding(FakeContainer(),TestRequest())
>>> adding.nameAllowed()
False
Fake class without IContainerNamesContainer
>>> class Fake(object):
... pass
nameAllowed returns True if the class
doesn't imlement IContainerNamesContainer
>>> adding = Adding(Fake(),TestRequest())
>>> adding.nameAllowed()
True
"""
def
test_chooseName
():
"""If user don't enter name, pick one
>>> class MyContainer(object):
... args = {}
... zope.interface.implements(INameChooser, IContainer)
... def chooseName(self, name, object):
... self.args["choose"] = name, object
... return 'pickone'
... def checkName(self, name, object):
... self.args["check"] = name, object
... def __setitem__(self, name, object):
... setattr(self, name, object)
... self.name = name
... def __getitem__(self, key):
... return getattr(self, key)
>>> request = TestRequest()
>>> mycontainer = MyContainer()
>>> adding = Adding(mycontainer, request)
>>> o = object()
>>> add_obj = adding.add(o)
>>> mycontainer.name
'pickone'
>>> add_obj is o
True
Make sure right arguments passed to INameChooser adapter:
>>> name, obj = mycontainer.args["choose"]
>>> name
''
>>> obj is o
True
>>> name, obj = mycontainer.args["check"]
>>> name
'pickone'
>>> obj is o
True
"""
def
test_SingleMenuItem_and_CustomAddView_NonICNC
():
"""
This tests the condition if the content has Custom Add views and
the container contains only a single content object
>>> registerAddMenu()
>>> defineMenuItem(AddMenu, IAdding, '', 'item3', extra={'factory': 'f1'})
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... if not isinstance(object, F1):
... raise zope.interface.Invalid()
>>> def prefactory(container, name, factory):
... if factory._callable is not F1:
... raise zope.interface.Invalid()
>>> pre.factory = prefactory
>>> class IContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(object):
... zope.interface.implements(IContainer)
>>> from zope.component.factory import Factory
>>> ztapi.provideUtility(IFactory, Factory(F1), 'f1')
>>> ztapi.provideUtility(IFactory, Factory(F2), 'f2')
>>> from zope.app.container.browser.adding import Adding
>>> adding = Adding(Container(), TestRequest())
>>> items = adding.addingInfo()
>>> len(items)
1
isSingleMenuItem returns True if there is only one content class
inside the Container
>>> adding.isSingleMenuItem()
True
hasCustomAddView will return False as the content does not have
a custom Add View
>>> adding.hasCustomAddView()
True
"""
def
test_SingleMenuItem_and_NoCustomAddView_NonICNC
():
"""
This function checks the case where there is a single content object
and there is non custom add view . Also the container does not
implement IContainerNamesContainer
>>> registerAddMenu()
>>> defineMenuItem(AddMenu, None, '', 'item3', extra={'factory': ''})
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... if not isinstance(object, F1):
... raise zope.interface.Invalid()
>>> def prefactory(container, name, factory):
... if factory._callable is not F1:
... raise zope.interface.Invalid()
>>> pre.factory = prefactory
>>> class IContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(object):
... zope.interface.implements(IContainer)
>>> from zope.component.factory import Factory
>>> ztapi.provideUtility(IFactory, Factory(F1), 'f1')
>>> ztapi.provideUtility(IFactory, Factory(F2), 'f2')
>>> from zope.app.container.browser.adding import Adding
>>> adding = Adding(Container(), TestRequest())
>>> items = adding.addingInfo()
>>> len(items)
1
The isSingleMenuItem will return True if there is one single content
that can be added inside the Container
>>> adding.isSingleMenuItem()
True
hasCustomAddView will return False as the content does not have
a custom Add View
>>> adding.hasCustomAddView()
False
"""
def
test_isSingleMenuItem_with_ICNC
():
"""
This test checks for whether there is a single content that can be added
and the container uses IContainerNamesContaienr
>>> registerAddMenu()
>>> defineMenuItem(AddMenu, None, '', 'item3', extra={'factory': ''})
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... if not isinstance(object, F1):
... raise zope.interface.Invalid()
>>> def prefactory(container, name, factory):
... if factory._callable is not F1:
... raise zope.interface.Invalid()
>>> pre.factory = prefactory
>>> class IContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(object):
... zope.interface.implements(IContainer, IContainerNamesContainer)
>>> from zope.app.container.browser.adding import Adding
>>> adding = Adding(Container(), TestRequest())
>>> items = adding.addingInfo()
>>> len(items)
1
>>> adding.isSingleMenuItem()
True
>>> adding.hasCustomAddView()
False
"""
def
test_suite
():
return
unittest
.
TestSuite
((
unittest
.
makeSuite
(
Test
),
DocTestSuite
(
setUp
=
setUp
,
tearDown
=
tearDown
),
))
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/browser/tests/test_contents.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Test Container Contents
$Id$
"""
from
unittest
import
TestCase
,
TestSuite
,
main
,
makeSuite
from
zope.interface
import
Interface
,
implements
from
zope.security
import
checker
from
zope.traversing.api
import
traverse
from
zope.annotation.interfaces
import
IAnnotations
from
zope.copypastemove
import
ContainerItemRenamer
from
zope.copypastemove
import
ObjectMover
,
ObjectCopier
from
zope.copypastemove
import
PrincipalClipboard
from
zope.copypastemove.interfaces
import
IContainerItemRenamer
from
zope.copypastemove.interfaces
import
IObjectMover
,
IObjectCopier
from
zope.copypastemove.interfaces
import
IPrincipalClipboard
from
zope.app.component.testing
import
PlacefulSetup
from
zope.app.container.contained
import
contained
from
zope.app.testing
import
ztapi
from
zope.app.container.interfaces
import
IContainer
,
IContained
class
BaseTestContentsBrowserView
(
PlacefulSetup
):
"""Base class for testing browser contents.
Subclasses need to define a method, '_TestView__newContext', that
takes no arguments and that returns a new empty test view context.
Subclasses need to define a method, '_TestView__newView', that
takes a context object and that returns a new test view.
"""
def
setUp
(
self
):
PlacefulSetup
.
setUp
(
self
)
PlacefulSetup
.
buildFolders
(
self
)
ztapi
.
provideAdapter
(
IContained
,
IObjectCopier
,
ObjectCopier
)
ztapi
.
provideAdapter
(
IContained
,
IObjectMover
,
ObjectMover
)
ztapi
.
provideAdapter
(
IContainer
,
IContainerItemRenamer
,
ContainerItemRenamer
)
ztapi
.
provideAdapter
(
IAnnotations
,
IPrincipalClipboard
,
PrincipalClipboard
)
ztapi
.
provideAdapter
(
Principal
,
IAnnotations
,
PrincipalAnnotations
)
def
testInfo
(
self
):
# Do we get the correct information back from ContainerContents?
container
=
self
.
_TestView__newContext
()
subcontainer
=
self
.
_TestView__newContext
()
container
[
'subcontainer'
]
=
subcontainer
document
=
Document
()
container
[
'document'
]
=
document
fc
=
self
.
_TestView__newView
(
container
)
info_list
=
fc
.
listContentInfo
()
self
.
assertEquals
(
len
(
info_list
),
2
)
ids
=
map
(
lambda
x
:
x
[
'id'
],
info_list
)
self
.
assert_
(
'subcontainer'
in
ids
)
objects
=
map
(
lambda
x
:
x
[
'object'
],
info_list
)
self
.
assert_
(
subcontainer
in
objects
)
urls
=
map
(
lambda
x
:
x
[
'url'
],
info_list
)
self
.
assert_
(
'subcontainer'
in
urls
)
self
.
failIf
(
filter
(
None
,
map
(
lambda
x
:
x
[
'icon'
],
info_list
)))
def
testInfoUnicode
(
self
):
# If the id contains non-ASCII characters, url has to be quoted
container
=
self
.
_TestView__newContext
()
subcontainer
=
self
.
_TestView__newContext
()
container
[
u'f
\
xf6
\
xf6
'
]
=
subcontainer
fc
=
self
.
_TestView__newView
(
container
)
info_list
=
fc
.
listContentInfo
()
urls
=
map
(
lambda
x
:
x
[
'url'
],
info_list
)
self
.
assert_
(
'f%C3%B6%C3%B6'
in
urls
)
def
testInfoWDublinCore
(
self
):
container
=
self
.
_TestView__newContext
()
document
=
Document
()
container
[
'document'
]
=
document
from
datetime
import
datetime
from
zope.dublincore.interfaces
import
IZopeDublinCore
class
FauxDCAdapter
(
object
):
implements
(
IZopeDublinCore
)
__Security_checker__
=
checker
.
Checker
(
{
"created"
:
"zope.Public"
,
"modified"
:
"zope.Public"
,
"title"
:
"zope.Public"
,
},
{
"title"
:
"zope.app.dublincore.change"
})
def
__init__
(
self
,
context
):
pass
title
=
'faux title'
size
=
1024
created
=
datetime
(
2001
,
1
,
1
,
1
,
1
,
1
)
modified
=
datetime
(
2002
,
2
,
2
,
2
,
2
,
2
)
ztapi
.
provideAdapter
(
IDocument
,
IZopeDublinCore
,
FauxDCAdapter
)
fc
=
self
.
_TestView__newView
(
container
)
info
=
fc
.
listContentInfo
()[
0
]
self
.
assertEqual
(
info
[
'id'
],
'document'
)
self
.
assertEqual
(
info
[
'url'
],
'document'
)
self
.
assertEqual
(
info
[
'object'
],
document
)
self
.
assertEqual
(
info
[
'title'
],
'faux title'
)
self
.
assertEqual
(
info
[
'created'
],
'01/01/01 01:01'
)
self
.
assertEqual
(
info
[
'modified'
],
'02/02/02 02:02'
)
def
testRemove
(
self
):
container
=
self
.
_TestView__newContext
()
subcontainer
=
self
.
_TestView__newContext
()
container
[
'subcontainer'
]
=
subcontainer
document
=
Document
()
container
[
'document'
]
=
document
document2
=
Document
()
container
[
'document2'
]
=
document2
fc
=
self
.
_TestView__newView
(
container
)
fc
.
request
.
form
.
update
({
'ids'
:
[
'document2'
]})
fc
.
removeObjects
()
info_list
=
fc
.
listContentInfo
()
self
.
assertEquals
(
len
(
info_list
),
2
)
ids
=
map
(
lambda
x
:
x
[
'id'
],
info_list
)
self
.
assert_
(
'subcontainer'
in
ids
)
objects
=
map
(
lambda
x
:
x
[
'object'
],
info_list
)
self
.
assert_
(
subcontainer
in
objects
)
urls
=
map
(
lambda
x
:
x
[
'url'
],
info_list
)
self
.
assert_
(
'subcontainer'
in
urls
)
class
IDocument
(
Interface
):
pass
class
Document
(
object
):
implements
(
IDocument
)
class
Principal
(
object
):
id
=
'bob'
class
PrincipalAnnotations
(
dict
):
implements
(
IAnnotations
)
data
=
{}
def
__new__
(
class_
,
context
):
try
:
annotations
=
class_
.
data
[
context
.
id
]
except
KeyError
:
annotations
=
dict
.
__new__
(
class_
)
class_
.
data
[
context
.
id
]
=
annotations
return
annotations
def
__init__
(
self
,
context
):
pass
def
__repr__
(
self
):
return
"<%s.PrincipalAnnotations object>"
%
__name__
class
TestCutCopyPaste
(
PlacefulSetup
,
TestCase
):
def
setUp
(
self
):
PlacefulSetup
.
setUp
(
self
)
PlacefulSetup
.
buildFolders
(
self
)
ztapi
.
provideAdapter
(
IContained
,
IObjectCopier
,
ObjectCopier
)
ztapi
.
provideAdapter
(
IContained
,
IObjectMover
,
ObjectMover
)
ztapi
.
provideAdapter
(
IContainer
,
IContainerItemRenamer
,
ContainerItemRenamer
)
ztapi
.
provideAdapter
(
IAnnotations
,
IPrincipalClipboard
,
PrincipalClipboard
)
ztapi
.
provideAdapter
(
Principal
,
IAnnotations
,
PrincipalAnnotations
)
def
testRename
(
self
):
container
=
traverse
(
self
.
rootFolder
,
'folder1'
)
fc
=
self
.
_TestView__newView
(
container
)
ids
=
[
'document1'
,
'document2'
]
for
id
in
ids
:
document
=
Document
()
container
[
id
]
=
document
fc
.
request
.
form
.
update
({
'rename_ids'
:
ids
,
'new_value'
:
[
'document1_1'
,
'document2_2'
]
})
fc
.
renameObjects
()
self
.
failIf
(
'document1_1'
not
in
container
)
self
.
failIf
(
'document1'
in
container
)
def
testCopyPaste
(
self
):
container
=
traverse
(
self
.
rootFolder
,
'folder1'
)
fc
=
self
.
_TestView__newView
(
container
)
ids
=
[
'document1'
,
'document2'
]
for
id
in
ids
:
document
=
Document
()
container
[
id
]
=
document
fc
.
request
.
form
[
'ids'
]
=
ids
fc
.
copyObjects
()
fc
.
pasteObjects
()
self
.
failIf
(
'document1'
not
in
container
)
self
.
failIf
(
'document2'
not
in
container
)
self
.
failIf
(
'document1-2'
not
in
container
)
self
.
failIf
(
'document2-2'
not
in
container
)
def
testCopyFolder
(
self
):
container
=
traverse
(
self
.
rootFolder
,
'folder1'
)
fc
=
self
.
_TestView__newView
(
container
)
ids
=
[
'folder1_1'
]
fc
.
request
.
form
[
'ids'
]
=
ids
fc
.
copyObjects
()
fc
.
pasteObjects
()
self
.
failIf
(
'folder1_1'
not
in
container
)
self
.
failIf
(
'folder1_1-2'
not
in
container
)
def
testCopyFolder2
(
self
):
container
=
traverse
(
self
.
rootFolder
,
'/folder1/folder1_1'
)
fc
=
self
.
_TestView__newView
(
container
)
ids
=
[
'folder1_1_1'
]
fc
.
request
.
form
[
'ids'
]
=
ids
fc
.
copyObjects
()
fc
.
pasteObjects
()
self
.
failIf
(
'folder1_1_1'
not
in
container
)
self
.
failIf
(
'folder1_1_1-2'
not
in
container
)
def
testCopyFolder3
(
self
):
container
=
traverse
(
self
.
rootFolder
,
'/folder1/folder1_1'
)
target
=
traverse
(
self
.
rootFolder
,
'/folder2/folder2_1'
)
fc
=
self
.
_TestView__newView
(
container
)
tg
=
self
.
_TestView__newView
(
target
)
ids
=
[
'folder1_1_1'
]
fc
.
request
.
form
[
'ids'
]
=
ids
fc
.
copyObjects
()
tg
.
pasteObjects
()
self
.
failIf
(
'folder1_1_1'
not
in
container
)
self
.
failIf
(
'folder1_1_1'
not
in
target
)
def
testCutPaste
(
self
):
container
=
traverse
(
self
.
rootFolder
,
'folder1'
)
fc
=
self
.
_TestView__newView
(
container
)
ids
=
[
'document1'
,
'document2'
]
for
id
in
ids
:
document
=
Document
()
container
[
id
]
=
document
fc
.
request
.
form
[
'ids'
]
=
ids
fc
.
cutObjects
()
fc
.
pasteObjects
()
self
.
failIf
(
'document1'
not
in
container
)
self
.
failIf
(
'document2'
not
in
container
)
def
testCutFolder
(
self
):
container
=
traverse
(
self
.
rootFolder
,
'folder1'
)
fc
=
self
.
_TestView__newView
(
container
)
ids
=
[
'folder1_1'
]
fc
.
request
.
form
[
'ids'
]
=
ids
fc
.
cutObjects
()
fc
.
pasteObjects
()
self
.
failIf
(
'folder1_1'
not
in
container
)
def
testCutFolder2
(
self
):
container
=
traverse
(
self
.
rootFolder
,
'/folder1/folder1_1'
)
fc
=
self
.
_TestView__newView
(
container
)
ids
=
[
'folder1_1_1'
]
fc
.
request
.
form
[
'ids'
]
=
ids
fc
.
cutObjects
()
fc
.
pasteObjects
()
self
.
failIf
(
'folder1_1_1'
not
in
container
)
def
testCutFolder3
(
self
):
container
=
traverse
(
self
.
rootFolder
,
'/folder1/folder1_1'
)
target
=
traverse
(
self
.
rootFolder
,
'/folder2/folder2_1'
)
fc
=
self
.
_TestView__newView
(
container
)
tg
=
self
.
_TestView__newView
(
target
)
ids
=
[
'folder1_1_1'
]
fc
.
request
.
form
[
'ids'
]
=
ids
fc
.
cutObjects
()
tg
.
pasteObjects
()
self
.
failIf
(
'folder1_1_1'
in
container
)
self
.
failIf
(
'folder1_1_1'
not
in
target
)
def
_TestView__newView
(
self
,
container
):
from
zope.app.container.browser.contents
import
Contents
from
zope.publisher.browser
import
TestRequest
request
=
TestRequest
()
request
.
setPrincipal
(
Principal
())
return
Contents
(
container
,
request
)
class
Test
(
BaseTestContentsBrowserView
,
TestCase
):
def
_TestView__newContext
(
self
):
from
zope.app.container.sample
import
SampleContainer
from
zope.app.folder
import
rootFolder
root
=
rootFolder
()
container
=
SampleContainer
()
return
contained
(
container
,
root
,
'sample'
)
def
_TestView__newView
(
self
,
container
):
from
zope.app.container.browser.contents
import
Contents
from
zope.publisher.browser
import
TestRequest
request
=
TestRequest
()
request
.
setPrincipal
(
Principal
())
return
Contents
(
container
,
request
)
def
test_suite
():
return
TestSuite
((
makeSuite
(
Test
),
makeSuite
(
TestCutCopyPaste
),
))
if
__name__
==
'__main__'
:
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/browser/tests/test_directive.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""'containerView' directive test
$Id$
"""
import
re
import
pprint
import
cStringIO
import
unittest
from
zope.interface
import
Interface
from
zope.publisher.interfaces.browser
import
IBrowserRequest
from
zope.testing.doctestunit
import
DocTestSuite
from
zope.app.container.browser.metaconfigure
import
containerViews
atre
=
re
.
compile
(
' at [0-9a-fA-Fx]+'
)
class
Context
(
object
):
actions
=
()
info
=
''
def
action
(
self
,
discriminator
,
callable
,
args
):
self
.
actions
+=
((
discriminator
,
callable
,
args
),
)
self
.
info
=
'info'
def
__repr__
(
self
):
stream
=
cStringIO
.
StringIO
()
pprinter
=
pprint
.
PrettyPrinter
(
stream
=
stream
,
width
=
60
)
pprinter
.
pprint
(
self
.
actions
)
r
=
stream
.
getvalue
()
return
(
''
.
join
(
atre
.
split
(
r
))).
strip
()
class
I
(
Interface
):
pass
class
ITestLayer
(
IBrowserRequest
):
pass
def
test_containerViews
():
"""
>>> from zope.app.publisher.browser.menumeta import menus
>>> from zope.interface.interface import InterfaceClass
>>> zmi_views = InterfaceClass('zmi_views', __module__='zope.app.menus')
>>> menus.zmi_views = zmi_views
>>> zmi_actions = InterfaceClass('zmi_actions', __module__='zope.app.menus')
>>> menus.zmi_actions = zmi_actions
>>> context = Context()
>>> containerViews(context, for_=I, contents='zope.ManageContent',
... add='zope.ManageContent', index='zope.View')
>>> context
((('adapter',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.app.menus.zmi_views>,
u'Contents'),
<function handler>,
('registerAdapter',
<zope.app.publisher.browser.menumeta.MenuItemFactory object>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.app.menus.zmi_views>,
u'Contents',
'')),
(None,
<function provideInterface>,
('', <InterfaceClass zope.app.menus.zmi_views>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(('view',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
'contents.html',
<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.Contents'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.interface.Interface>,
'contents.html',
'info')),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(('view',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
'index.html',
<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.Contents'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.interface.Interface>,
'index.html',
'info')),
(('adapter',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.app.menus.zmi_actions>,
u'Add'),
<function handler>,
('registerAdapter',
<zope.app.publisher.browser.menumeta.MenuItemFactory object>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.app.menus.zmi_actions>,
u'Add',
'info')),
(None,
<function provideInterface>,
('', <InterfaceClass zope.app.menus.zmi_actions>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('', <InterfaceClass zope.interface.Interface>)),
(('view',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
'+',
<InterfaceClass zope.interface.Interface>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.+'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.interface.Interface>,
'+',
'info')))
"""
def
test_containerViews_layer
():
"""
>>> from zope.app.publisher.browser.menumeta import menus
>>> from zope.interface.interface import InterfaceClass
>>> zmi_views = InterfaceClass('zmi_views', __module__='zope.app.menus')
>>> menus.zmi_views = zmi_views
>>> zmi_actions = InterfaceClass('zmi_actions', __module__='zope.app.menus')
>>> menus.zmi_actions = zmi_actions
>>> context = Context()
>>> containerViews(context, for_=I, contents='zope.ManageContent',
... add='zope.ManageContent', index='zope.View', layer=ITestLayer)
>>> context
((('adapter',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.app.menus.zmi_views>,
u'Contents'),
<function handler>,
('registerAdapter',
<zope.app.publisher.browser.menumeta.MenuItemFactory object>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.app.menus.zmi_views>,
u'Contents',
'')),
(None,
<function provideInterface>,
('', <InterfaceClass zope.app.menus.zmi_views>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(('view',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
'contents.html',
<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.Contents'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.interface.Interface>,
'contents.html',
'info')),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(('view',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
'index.html',
<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.Contents'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.interface.Interface>,
'index.html',
'info')),
(('adapter',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.app.menus.zmi_actions>,
u'Add'),
<function handler>,
('registerAdapter',
<zope.app.publisher.browser.menumeta.MenuItemFactory object>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.app.menus.zmi_actions>,
u'Add',
'info')),
(None,
<function provideInterface>,
('', <InterfaceClass zope.app.menus.zmi_actions>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('', <InterfaceClass zope.interface.Interface>)),
(('view',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
'+',
<InterfaceClass zope.interface.Interface>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.+'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.interface.Interface>,
'+',
'info')))
"""
def
test_suite
():
return
unittest
.
TestSuite
((
DocTestSuite
(),
))
if
__name__
==
'__main__'
:
unittest
.
main
()
src/zope/app/container/btree.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""This module provides a sample container implementation.
This is primarily for testing purposes.
It might be useful as a mix-in for some classes, but many classes will
need a very different implementation.
$Id$
"""
__docformat__
=
'restructuredtext'
from
persistent
import
Persistent
from
BTrees.OOBTree
import
OOBTree
from
zope.app.container.sample
import
SampleContainer
class
BTreeContainer
(
SampleContainer
,
Persistent
):
# implements(what my base classes implement)
# TODO: It appears that BTreeContainer uses SampleContainer only to
# get the implementation of __setitem__(). All the other methods
# provided by that base class are just slower replacements for
# operations on the BTree itself. It would probably be clearer to
# just delegate those methods directly to the btree.
def
_newContainerData
(
self
):
"""Construct an item-data container
Subclasses should override this if they want different data.
The value returned is a mapping object that also has get,
has_key, keys, items, and values methods.
"""
return
OOBTree
()
def
__contains__
(
self
,
key
):
'''See interface IReadContainer
Reimplement this method, since has_key() returns the key if available,
while we expect True or False.
>>> c = BTreeContainer()
>>> "a" in c
False
>>> c["a"] = 1
>>> "a" in c
True
>>> "A" in c
False
'''
return
key
in
self
.
_SampleContainer__data
has_key
=
__contains__
src/zope/app/container/configure.zcml
0 → 100644
View file @
160df616
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
i18n_domain="zope"
>
<adapter
provides=".interfaces.IFind"
for=".interfaces.IReadContainer"
permission="zope.ManageContent"
factory="zope.app.container.find.FindAdapter"
/>
<adapter
for=".interfaces.IReadContainer"
provides="zope.filerepresentation.interfaces.IReadDirectory"
factory=".directory.noop"
/>
<adapter
for=".interfaces.IWriteContainer"
provides="zope.filerepresentation.interfaces.IWriteDirectory"
factory=".directory.noop"
/>
<adapter
factory="zope.app.container.traversal.ContainerTraversable"
provides="zope.traversing.interfaces.ITraversable"
for="zope.app.container.interfaces.IReadContainer"
/>
<adapter
factory="zope.app.container.size.ContainerSized"
provides="zope.size.interfaces.ISized"
for="zope.app.container.interfaces.IReadContainer"
/>
<adapter
provides=".interfaces.INameChooser"
for="zope.app.container.interfaces.IWriteContainer"
factory=".contained.NameChooser"
/>
<subscriber
handler=".dependency.CheckDependency"
for="zope.app.container.interfaces.IObjectRemovedEvent"
trusted="y"
/>
<subscriber
for="zope.location.interfaces.ILocation
zope.app.container.interfaces.IObjectMovedEvent"
handler=".contained.dispatchToSublocations"
>
Handler dispatches moved events to sublocations of the original object.
</subscriber>
<adapter
provides="zope.location.interfaces.ISublocations"
for="zope.app.container.interfaces.IReadContainer"
factory=".contained.ContainerSublocations"
/>
<class class=".constraints.ItemTypePrecondition">
<allow interface=".constraints.IItemTypePrecondition" />
</class>
<view
for="zope.app.container.interfaces.IItemContainer"
type="zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.publisher.interfaces.browser.IBrowserPublisher"
factory="zope.app.container.traversal.ItemTraverser"
permission="zope.Public"
allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
/>
<view
for="zope.app.container.interfaces.ISimpleReadContainer"
type="zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.publisher.interfaces.browser.IBrowserPublisher"
factory="zope.app.container.traversal.ItemTraverser"
permission="zope.Public"
allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
/>
</configure>
src/zope/app/container/constraints.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Support for containment constraints
Either a container or an object can provide constraints on the
containment relationship.
A container expresses constraints through a precondition on it's
`__setitem__` method in it's interface.
Preconditions can be simple callable objects, like functions. They
should raise a ``zope.interface.Invalid`` exception to indicate that a
constraint isn't satisfied:
>>> def preNoZ(container, name, ob):
... "Silly precondition example"
... if name.startswith("Z"):
... raise zope.interface.Invalid("Names can not start with Z")
>>> class I1(zope.interface.Interface):
... def __setitem__(name, on):
... "Add an item"
... __setitem__.precondition = preNoZ
>>> from zope.app.container.interfaces import IContainer
>>> class C1(object):
... zope.interface.implements(I1, IContainer)
... def __repr__(self):
... return 'C1'
Given such a precondition, we can then check whether an object can be
added:
>>> c1 = C1()
>>> checkObject(c1, "bob", None)
>>> checkObject(c1, "Zbob", None)
Traceback (most recent call last):
...
Invalid: Names can not start with Z
We can also express constaints on the containers an object can be
added to. We do this by setting a field constraint on an object's
`__parent__` attribute:
>>> import zope.schema
A field constraint is a callable object that returns a boolean value:
>>> def con1(container):
... "silly container constraint"
... if not hasattr(container, 'x'):
... return False
... return True
>>> class I2(zope.interface.Interface):
... __parent__ = zope.schema.Field(constraint = con1)
>>> class O(object):
... zope.interface.implements(I2)
If the constraint isn't satisfied, we'll get a validation error when we
check whether the object can be added:
>>> checkObject(c1, "bob", O())
Traceback (most recent call last):
...
ConstraintNotSatisfied: C1
Note that the validation error isn't very informative. For that
reason, it's better for constraints to raise Invalid errors when they
aren't satisfied:
>>> def con1(container):
... "silly container constraint"
... if not hasattr(container, 'x'):
... raise zope.interface.Invalid("What, no x?")
... return True
>>> class I2(zope.interface.Interface):
... __parent__ = zope.schema.Field(constraint = con1)
>>> class O(object):
... zope.interface.implements(I2)
>>> checkObject(c1, "bob", O())
Traceback (most recent call last):
...
Invalid: What, no x?
>>> c1.x = 1
>>> checkObject(c1, "bob", O())
The `checkObject` function is handy when checking whether we can add an
existing object to a container, but, sometimes, we want to check
whether an object produced by a factory can be added. To do this, we
use `checkFactory`:
>>> class Factory(object):
... def __call__(self):
... return O()
... def getInterfaces(self):
... return zope.interface.implementedBy(O)
>>> factory = Factory()
>>> checkFactory(c1, "bob", factory)
True
>>> del c1.x
>>> checkFactory(c1, "bob", factory)
False
Unlike `checkObject`, `checkFactory`:
- Returns a boolean value
- Takes a factory (e.g. a class) rather than an argument.
The container constraint we defined for C1 isn't actually used to
check the factory:
>>> c1.x = 1
>>> checkFactory(c1, "Zbob", factory)
True
To work with `checkFactory`, a container precondition has to
implement a factory method. This is because a factory, rather than
an object is passed. To illustrate this, we'll make preNoZ its own
factory method:
>>> preNoZ.factory = preNoZ
We can do this (silly thing) because preNoZ doesn't use the object
argument.
>>> checkFactory(c1, "Zbob", factory)
False
$Id$
"""
__docformat__
=
'restructuredtext'
import
sys
from
zope.cachedescriptors.property
import
readproperty
from
zope.dottedname.resolve
import
resolve
import
zope.schema
from
zope.interface
import
providedBy
from
zope.app.container.interfaces
import
InvalidItemType
,
InvalidContainerType
from
zope.app.container.i18n
import
ZopeMessageFactory
as
_
from
zope.app.container.interfaces
import
IContainer
def
checkObject
(
container
,
name
,
object
):
"""Check containement constraints for an object and container
"""
# check __setitem__ precondition
containerProvided
=
providedBy
(
container
)
__setitem__
=
containerProvided
.
get
(
'__setitem__'
)
if
__setitem__
is
not
None
:
precondition
=
__setitem__
.
queryTaggedValue
(
'precondition'
)
if
precondition
is
not
None
:
precondition
(
container
,
name
,
object
)
# check the constraint on __parent__
__parent__
=
providedBy
(
object
).
get
(
'__parent__'
)
if
__parent__
is
not
None
:
try
:
validate
=
__parent__
.
validate
except
AttributeError
:
pass
else
:
validate
(
container
)
if
not
containerProvided
.
extends
(
IContainer
):
# If it doesn't implement IContainer, it can't contain stuff.
raise
TypeError
(
_
(
'Container is not a valid Zope container.'
)
)
def
checkFactory
(
container
,
name
,
factory
):
__setitem__
=
providedBy
(
container
).
get
(
'__setitem__'
)
if
__setitem__
is
not
None
:
precondition
=
__setitem__
.
queryTaggedValue
(
'precondition'
)
if
precondition
is
not
None
:
try
:
precondition
=
precondition
.
factory
except
AttributeError
:
pass
else
:
try
:
precondition
(
container
,
name
,
factory
)
except
zope
.
interface
.
Invalid
:
return
False
# check the constraint on __parent__
__parent__
=
factory
.
getInterfaces
().
get
(
'__parent__'
)
if
__parent__
is
not
None
:
try
:
validate
=
__parent__
.
validate
except
AttributeError
:
pass
else
:
try
:
validate
(
container
)
except
zope
.
interface
.
Invalid
:
return
False
return
True
class
IItemTypePrecondition
(
zope
.
interface
.
Interface
):
def
__call__
(
container
,
name
,
object
):
"""Test whether container setitem arguments are valid.
Raise zope.interface.Invalid if the object is invalid.
"""
def
factory
(
container
,
name
,
factory
):
"""Test whether objects provided by the factory are acceptable
Return a boolean value.
"""
class
_TypesBased
(
object
):
@
readproperty
def
types
(
self
):
raw_types
,
module
=
self
.
raw_types
types
=
[]
for
t
in
raw_types
:
if
isinstance
(
t
,
str
):
t
=
resolve
(
t
,
module
)
types
.
append
(
t
)
self
.
types
=
types
return
types
def
__init__
(
self
,
*
types
,
**
kw
):
if
[
t
for
t
in
types
if
isinstance
(
t
,
str
)]:
# have dotted names
module
=
kw
.
get
(
'module'
,
sys
.
_getframe
(
1
).
f_globals
[
'__name__'
])
self
.
raw_types
=
types
,
module
else
:
self
.
types
=
types
class
ItemTypePrecondition
(
_TypesBased
):
"""Specify a `__setitem__` precondition that restricts item types
Items must be one of the given types.
>>> class I1(zope.interface.Interface):
... pass
>>> class I2(zope.interface.Interface):
... pass
>>> precondition = ItemTypePrecondition(I1, I2)
>>> class Ob(object):
... pass
>>> ob = Ob()
>>> class Factory(object):
... def __call__(self):
... return Ob()
... def getInterfaces(self):
... return zope.interface.implementedBy(Ob)
>>> factory = Factory()
>>> try:
... precondition(None, 'foo', ob)
... except InvalidItemType, v:
... print v[0], (v[1] is ob), (v[2] == (I1, I2))
... else:
... print 'Should have failed'
None True True
>>> try:
... precondition.factory(None, 'foo', factory)
... except InvalidItemType, v:
... print v[0], (v[1] is factory), (v[2] == (I1, I2))
... else:
... print 'Should have failed'
None True True
>>> zope.interface.classImplements(Ob, I2)
>>> precondition(None, 'foo', ob)
>>> precondition.factory(None, 'foo', factory)
"""
zope
.
interface
.
implements
(
IItemTypePrecondition
)
def
__call__
(
self
,
container
,
name
,
object
):
for
iface
in
self
.
types
:
if
iface
.
providedBy
(
object
):
return
raise
InvalidItemType
(
container
,
object
,
self
.
types
)
def
factory
(
self
,
container
,
name
,
factory
):
implemented
=
factory
.
getInterfaces
()
for
iface
in
self
.
types
:
if
implemented
.
isOrExtends
(
iface
):
return
raise
InvalidItemType
(
container
,
factory
,
self
.
types
)
def
contains
(
*
types
):
"""Declare that a container type contains only the given types
This is used within a class suite defining an interface to create
a __setitem__ specification with a precondition allowing only the
given types:
>>> class IFoo(zope.interface.Interface):
... pass
>>> class IBar(zope.interface.Interface):
... pass
>>> class IFooBarContainer(IContainer):
... contains(IFoo, IBar)
>>> __setitem__ = IFooBarContainer['__setitem__']
>>> __setitem__.getTaggedValue('precondition').types == (IFoo, IBar)
True
It is invalid to call contains outside a class suite:
>>> contains(IFoo, IBar)
Traceback (most recent call last):
...
TypeError: contains not called from suite
"""
frame
=
sys
.
_getframe
(
1
)
f_locals
=
frame
.
f_locals
f_globals
=
frame
.
f_globals
if
not
(
f_locals
is
not
f_globals
and
f_locals
.
get
(
'__module__'
)
and
f_locals
.
get
(
'__module__'
)
==
f_globals
.
get
(
'__name__'
)
):
raise
TypeError
(
"contains not called from suite"
)
def
__setitem__
(
key
,
value
):
pass
__setitem__
.
__doc__
=
IContainer
[
'__setitem__'
].
__doc__
__setitem__
.
precondition
=
ItemTypePrecondition
(
*
types
,
**
dict
(
module
=
f_globals
[
'__name__'
])
)
f_locals
[
'__setitem__'
]
=
__setitem__
class
IContainerTypesConstraint
(
zope
.
interface
.
Interface
):
def
__call__
(
object
):
"""Test whether object is valid.
Return True if valid.
Raise zope.interface.Invalid if the objet is invalid.
"""
class
ContainerTypesConstraint
(
_TypesBased
):
"""Constrain a container to be one of a number of types
>>> class I1(zope.interface.Interface):
... pass
>>> class I2(zope.interface.Interface):
... pass
>>> class Ob(object):
... pass
>>> ob = Ob()
>>> constraint = ContainerTypesConstraint(I1, I2)
>>> try:
... constraint(ob)
... except InvalidContainerType, v:
... print (v[0] is ob), (v[1] == (I1, I2))
... else:
... print 'Should have failed'
True True
>>> zope.interface.classImplements(Ob, I2)
>>> constraint(Ob())
True
"""
zope
.
interface
.
implements
(
IContainerTypesConstraint
)
def
__call__
(
self
,
object
):
for
iface
in
self
.
types
:
if
iface
.
providedBy
(
object
):
return
True
else
:
raise
InvalidContainerType
(
object
,
self
.
types
)
def
containers
(
*
types
):
"""Declare the container types a type can be contained in
This is used within a class suite defining an interface to create
a __parent__ specification with a constraint allowing only the
given types:
>>> class IFoo(IContainer):
... pass
>>> class IBar(IContainer):
... pass
>>> from zope.app.container.interfaces import IContained
>>> class IFooBarContained(IContained):
... containers(IFoo, IBar)
>>> __parent__ = IFooBarContained['__parent__']
>>> __parent__.constraint.types == (IFoo, IBar)
True
It is invalid to call containers outside a class suite:
>>> containers(IFoo, IBar)
Traceback (most recent call last):
...
TypeError: containers not called from suite
"""
frame
=
sys
.
_getframe
(
1
)
f_locals
=
frame
.
f_locals
f_globals
=
frame
.
f_globals
if
not
(
f_locals
is
not
f_globals
and
f_locals
.
get
(
'__module__'
)
and
f_locals
.
get
(
'__module__'
)
==
f_globals
.
get
(
'__name__'
)
):
raise
TypeError
(
"containers not called from suite"
)
__parent__
=
zope
.
schema
.
Field
(
constraint
=
ContainerTypesConstraint
(
*
types
,
**
dict
(
module
=
f_globals
[
'__name__'
])
)
)
f_locals
[
'__parent__'
]
=
__parent__
src/zope/app/container/constraints.txt
0 → 100644
View file @
160df616
Containment constraints
=======================
Containment constraints allow us to express restrictions on the types
of items that can be placed in containers or on the types of
containers an item can be placed in. We express these constraints in
interfaces. Let's define some container and item interfaces:
>>> from zope.app.container.interfaces import IContainer, IContained
>>> from zope.app.container.constraints import containers, contains
>>> class IBuddyFolder(IContainer):
... contains('.IBuddy')
In this example, we used the contains function to declare that objects
that provide IBuddyFolder can only contain items that provide IBuddy.
Note that we used a string containing a dotted name for the IBuddy
interface. This is because IBuddy hasn't been defined yet. When we
define IBuddy, we can use IBuddyFolder directly:
>>> class IBuddy(IContained):
... containers(IBuddyFolder)
Now, with these interfaces in place, we can define Buddy and
BuddyFolder classes and verify that we can put buddies in buddy
folders:
>>> from zope import interface
>>> class Buddy:
... interface.implements(IBuddy)
>>> class BuddyFolder:
... interface.implements(IBuddyFolder)
>>> from zope.app.container.constraints import checkObject, checkFactory
>>> from zope.component.factory import Factory
>>> checkObject(BuddyFolder(), 'x', Buddy())
>>> checkFactory(BuddyFolder(), 'x', Factory(Buddy))
True
If we try to use other containers or folders, we'll get errors:
>>> class Container:
... interface.implements(IContainer)
>>> class Contained:
... interface.implements(IContained)
>>> checkObject(Container(), 'x', Buddy())
... # doctest: +ELLIPSIS
Traceback (most recent call last):
InvalidContainerType: ...
>>> checkFactory(Container(), 'x', Factory(Buddy))
False
>>> checkObject(BuddyFolder(), 'x', Contained())
... # doctest: +ELLIPSIS
Traceback (most recent call last):
InvalidItemType: ...
>>> checkFactory(BuddyFolder(), 'x', Factory(Contained))
False
In the example, we defined the container first and then the items. We
could have defined these in the opposite order:
>>> class IContact(IContained):
... containers('.IContacts')
>>> class IContacts(IContainer):
... contains(IContact)
>>> class Contact:
... interface.implements(IContact)
>>> class Contacts:
... interface.implements(IContacts)
>>> checkObject(Contacts(), 'x', Contact())
>>> checkFactory(Contacts(), 'x', Factory(Contact))
True
>>> checkObject(Contacts(), 'x', Buddy())
... # doctest: +ELLIPSIS
Traceback (most recent call last):
InvalidItemType: ...
>>> checkFactory(Contacts(), 'x', Factory(Buddy))
False
src/zope/app/container/contained.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Classes to support implementing `IContained`
$Id$
"""
__docformat__
=
'restructuredtext'
import
zope.component
import
zope.interface.declarations
from
zope.interface
import
providedBy
from
zope.interface.declarations
import
getObjectSpecification
from
zope.interface.declarations
import
ObjectSpecification
from
zope.event
import
notify
from
zope.component.interfaces
import
ObjectEvent
from
zope.location.interfaces
import
ILocation
,
ISublocations
from
zope.exceptions.interfaces
import
DuplicationError
,
UserError
from
zope.security.checker
import
selectChecker
,
CombinedChecker
from
zope.lifecycleevent
import
ObjectModifiedEvent
from
zope.app.container.i18n
import
ZopeMessageFactory
as
_
from
zope.app.container.interfaces
import
IContained
from
zope.app.container.interfaces
import
INameChooser
from
zope.app.container.interfaces
import
IObjectAddedEvent
from
zope.app.container.interfaces
import
IObjectMovedEvent
from
zope.app.container.interfaces
import
IObjectRemovedEvent
from
zope.app.container.interfaces
import
IContainerModifiedEvent
from
zope.app.container._zope_app_container_contained
import
ContainedProxyBase
from
zope.app.container._zope_app_container_contained
import
getProxiedObject
from
zope.app.broken.interfaces
import
IBroken
class
Contained
(
object
):
"""Stupid mix-in that defines `__parent__` and `__name__` attributes"""
zope
.
interface
.
implements
(
IContained
)
__parent__
=
__name__
=
None
class
ObjectMovedEvent
(
ObjectEvent
):
"""An object has been moved"""
zope
.
interface
.
implements
(
IObjectMovedEvent
)
def
__init__
(
self
,
object
,
oldParent
,
oldName
,
newParent
,
newName
):
ObjectEvent
.
__init__
(
self
,
object
)
self
.
oldParent
=
oldParent
self
.
oldName
=
oldName
self
.
newParent
=
newParent
self
.
newName
=
newName
class
ObjectAddedEvent
(
ObjectMovedEvent
):
"""An object has been added to a container"""
zope
.
interface
.
implements
(
IObjectAddedEvent
)
def
__init__
(
self
,
object
,
newParent
=
None
,
newName
=
None
):
if
newParent
is
None
:
newParent
=
object
.
__parent__
if
newName
is
None
:
newName
=
object
.
__name__
ObjectMovedEvent
.
__init__
(
self
,
object
,
None
,
None
,
newParent
,
newName
)
class
ObjectRemovedEvent
(
ObjectMovedEvent
):
"""An object has been removed from a container"""
zope
.
interface
.
implements
(
IObjectRemovedEvent
)
def
__init__
(
self
,
object
,
oldParent
=
None
,
oldName
=
None
):
if
oldParent
is
None
:
oldParent
=
object
.
__parent__
if
oldName
is
None
:
oldName
=
object
.
__name__
ObjectMovedEvent
.
__init__
(
self
,
object
,
oldParent
,
oldName
,
None
,
None
)
class
ContainerModifiedEvent
(
ObjectModifiedEvent
):
"""The container has been modified."""
zope
.
interface
.
implements
(
IContainerModifiedEvent
)
def
dispatchToSublocations
(
object
,
event
):
"""Dispatch an event to sublocations of a given object
When a move event happens for an object, it's important to notify
subobjects as well.
We do this based on locations.
Suppose, for example, that we define some location objects.
>>> class L(object):
... zope.interface.implements(ILocation)
... def __init__(self, name):
... self.__name__ = name
... self.__parent__ = None
... def __repr__(self):
... return '%s(%s)' % (
... self.__class__.__name__, str(self.__name__))
>>> class C(L):
... zope.interface.implements(ISublocations)
... def __init__(self, name, *subs):
... L.__init__(self, name)
... self.subs = subs
... for sub in subs:
... sub.__parent__ = self
... def sublocations(self):
... return self.subs
>>> c = C(1,
... C(11,
... L(111),
... L(112),
... ),
... C(12,
... L(121),
... L(122),
... L(123),
... L(124),
... ),
... L(13),
... )
Now, if we call the dispatcher, it should call event handlers
for all of the objects.
Lets create an event handler that records the objects it sees:
>>> seen = []
>>> def handler(ob, event):
... seen.append((ob, event.object))
Note that we record the the object the handler is called on as
well as the event object:
Now we'll register it:
>>> from zope.app.testing import ztapi
>>> ztapi.subscribe([None, IObjectMovedEvent], None, handler)
We also register our dispatcher:
>>> ztapi.subscribe([None, IObjectMovedEvent], None,
... dispatchToSublocations)
We can then call the dispatcher for the root object:
>>> event = ObjectRemovedEvent(c)
>>> dispatchToSublocations(c, event)
Now, we should have seen all of the subobjects:
>>> seenreprs = map(repr, seen)
>>> seenreprs.sort()
>>> seenreprs
['(C(11), C(1))', '(C(12), C(1))', '(L(111), C(1))',"""
\
""" '(L(112), C(1))', '(L(121), C(1))', '(L(122), C(1))',"""
\
""" '(L(123), C(1))', '(L(124), C(1))', '(L(13), C(1))']
We see that we get entries for each of the subobjects and
that,for each entry, the event object is top object.
This suggests that location event handlers need to be aware that
the objects they are called on and the event objects could be
different.
"""
subs
=
ISublocations
(
object
,
None
)
if
subs
is
not
None
:
for
sub
in
subs
.
sublocations
():
for
ignored
in
zope
.
component
.
subscribers
((
sub
,
event
),
None
):
pass
# They do work in the adapter fetch
class
ContainerSublocations
(
object
):
"""Get the sublocations for a container
Obviously, this is the container values:
>>> class MyContainer(object):
... def __init__(self, **data):
... self.data = data
... def __iter__(self):
... return iter(self.data)
... def __getitem__(self, key):
... return self.data[key]
>>> container = MyContainer(x=1, y=2, z=42)
>>> adapter = ContainerSublocations(container)
>>> sublocations = list(adapter.sublocations())
>>> sublocations.sort()
>>> sublocations
[1, 2, 42]
"""
def
__init__
(
self
,
container
):
self
.
container
=
container
def
sublocations
(
self
):
container
=
self
.
container
for
key
in
container
:
yield
container
[
key
]
def
containedEvent
(
object
,
container
,
name
=
None
):
"""Establish the containment of the object in the container
The object and necessary event are returned. The object may be a
`ContainedProxy` around the original object. The event is an added
event, a moved event, or None.
If the object implements `IContained`, simply set its `__parent__`
and `__name__` attributes:
>>> container = {}
>>> item = Contained()
>>> x, event = containedEvent(item, container, u'foo')
>>> x is item
True
>>> item.__parent__ is container
True
>>> item.__name__
u'foo'
We have an added event:
>>> event.__class__.__name__
'ObjectAddedEvent'
>>> event.object is item
True
>>> event.newParent is container
True
>>> event.newName
u'foo'
>>> event.oldParent
>>> event.oldName
Now if we call contained again:
>>> x2, event = containedEvent(item, container, u'foo')
>>> x2 is item
True
>>> item.__parent__ is container
True
>>> item.__name__
u'foo'
We don't get a new added event:
>>> event
If the object already had a parent but the parent or name was
different, we get a moved event:
>>> x, event = containedEvent(item, container, u'foo2')
>>> event.__class__.__name__
'ObjectMovedEvent'
>>> event.object is item
True
>>> event.newParent is container
True
>>> event.newName
u'foo2'
>>> event.oldParent is container
True
>>> event.oldName
u'foo'
If the `object` implements `ILocation`, but not `IContained`, set it's
`__parent__` and `__name__` attributes *and* declare that it
implements `IContained`:
>>> from zope.location import Location
>>> item = Location()
>>> IContained.providedBy(item)
False
>>> x, event = containedEvent(item, container, 'foo')
>>> x is item
True
>>> item.__parent__ is container
True
>>> item.__name__
'foo'
>>> IContained.providedBy(item)
True
If the `object` doesn't even implement `ILocation`, put a
`ContainedProxy` around it:
>>> item = []
>>> x, event = containedEvent(item, container, 'foo')
>>> x is item
False
>>> x.__parent__ is container
True
>>> x.__name__
'foo'
Make sure we don't lose existing directly provided interfaces.
>>> from zope.interface import Interface, directlyProvides
>>> class IOther(Interface):
... pass
>>> from zope.location import Location
>>> item = Location()
>>> directlyProvides(item, IOther)
>>> IOther.providedBy(item)
True
>>> x, event = containedEvent(item, container, 'foo')
>>> IOther.providedBy(item)
True
"""
if
not
IContained
.
providedBy
(
object
):
if
ILocation
.
providedBy
(
object
):
zope
.
interface
.
directlyProvides
(
object
,
IContained
,
zope
.
interface
.
directlyProvidedBy
(
object
))
else
:
object
=
ContainedProxy
(
object
)
oldparent
=
object
.
__parent__
oldname
=
object
.
__name__
if
oldparent
is
container
and
oldname
==
name
:
# No events
return
object
,
None
object
.
__parent__
=
container
object
.
__name__
=
name
if
oldparent
is
None
or
oldname
is
None
:
event
=
ObjectAddedEvent
(
object
,
container
,
name
)
else
:
event
=
ObjectMovedEvent
(
object
,
oldparent
,
oldname
,
container
,
name
)
return
object
,
event
def
contained
(
object
,
container
,
name
=
None
):
"""Establish the containment of the object in the container
Just return the contained object without an event. This is a convenience
"macro" for:
``containedEvent(object, container, name)[0]``
This function is only used for tests.
"""
return
containedEvent
(
object
,
container
,
name
)[
0
]
def
notifyContainerModified
(
object
,
*
descriptions
):
"""Notify that the container was modified."""
notify
(
ContainerModifiedEvent
(
object
,
*
descriptions
))
def
setitem
(
container
,
setitemf
,
name
,
object
):
"""Helper function to set an item and generate needed events
This helper is needed, in part, because the events need to get
published after the `object` has been added to the `container`.
If the item implements `IContained`, simply set its `__parent__`
and `__name__` attributes:
>>> class IItem(zope.interface.Interface):
... pass
>>> class Item(Contained):
... zope.interface.implements(IItem)
... def setAdded(self, event):
... self.added = event
... def setMoved(self, event):
... self.moved = event
>>> from zope.app.container.interfaces import IObjectAddedEvent
>>> from zope.app.container.interfaces import IObjectMovedEvent
>>> from zope.app.testing import ztapi
>>> ztapi.subscribe([IItem, IObjectAddedEvent], None,
... lambda obj, event: obj.setAdded(event))
>>> ztapi.subscribe([IItem, IObjectMovedEvent], None,
... lambda obj, event: obj.setMoved(event))
>>> item = Item()
>>> container = {}
>>> setitem(container, container.__setitem__, u'c', item)
>>> container[u'c'] is item
1
>>> item.__parent__ is container
1
>>> item.__name__
u'c'
If we run this using the testing framework, we'll use `getEvents` to
track the events generated:
>>> from zope.component.eventtesting import getEvents
>>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent
We have an added event:
>>> len(getEvents(IObjectAddedEvent))
1
>>> event = getEvents(IObjectAddedEvent)[-1]
>>> event.object is item
1
>>> event.newParent is container
1
>>> event.newName
u'c'
>>> event.oldParent
>>> event.oldName
As well as a modification event for the container:
>>> len(getEvents(IObjectModifiedEvent))
1
>>> getEvents(IObjectModifiedEvent)[-1].object is container
1
The item's hooks have been called:
>>> item.added is event
1
>>> item.moved is event
1
We can suppress events and hooks by setting the `__parent__` and
`__name__` first:
>>> item = Item()
>>> item.__parent__, item.__name__ = container, 'c2'
>>> setitem(container, container.__setitem__, u'c2', item)
>>> len(container)
2
>>> len(getEvents(IObjectAddedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
1
>>> getattr(item, 'added', None)
>>> getattr(item, 'moved', None)
If the item had a parent or name (as in a move or rename),
we generate a move event, rather than an add event:
>>> setitem(container, container.__setitem__, u'c3', item)
>>> len(container)
3
>>> len(getEvents(IObjectAddedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
2
>>> len(getEvents(IObjectMovedEvent))
2
(Note that we have 2 move events because add are move events.)
We also get the move hook called, but not the add hook:
>>> event = getEvents(IObjectMovedEvent)[-1]
>>> getattr(item, 'added', None)
>>> item.moved is event
1
If we try to replace an item without deleting it first, we'll get
an error:
>>> setitem(container, container.__setitem__, u'c', [])
Traceback (most recent call last):
...
DuplicationError: c
>>> del container[u'c']
>>> setitem(container, container.__setitem__, u'c', [])
>>> len(getEvents(IObjectAddedEvent))
2
>>> len(getEvents(IObjectModifiedEvent))
3
If the object implements `ILocation`, but not `IContained`, set it's
`__parent__` and `__name__` attributes *and* declare that it
implements `IContained`:
>>> from zope.location import Location
>>> item = Location()
>>> IContained.providedBy(item)
0
>>> setitem(container, container.__setitem__, u'l', item)
>>> container[u'l'] is item
1
>>> item.__parent__ is container
1
>>> item.__name__
u'l'
>>> IContained.providedBy(item)
1
We get new added and modification events:
>>> len(getEvents(IObjectAddedEvent))
3
>>> len(getEvents(IObjectModifiedEvent))
4
If the object doesn't even implement `ILocation`, put a
`ContainedProxy` around it:
>>> item = []
>>> setitem(container, container.__setitem__, u'i', item)
>>> container[u'i']
[]
>>> container[u'i'] is item
0
>>> item = container[u'i']
>>> item.__parent__ is container
1
>>> item.__name__
u'i'
>>> IContained.providedBy(item)
1
>>> len(getEvents(IObjectAddedEvent))
4
>>> len(getEvents(IObjectModifiedEvent))
5
We'll get type errors if we give keys that aren't unicode or ascii keys:
>>> setitem(container, container.__setitem__, 42, item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string
>>> setitem(container, container.__setitem__, None, item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string
>>> setitem(container, container.__setitem__, 'hello ' + chr(200), item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string
and we'll get a value error of we give an empty string or unicode:
>>> setitem(container, container.__setitem__, '', item)
Traceback (most recent call last):
...
ValueError: empty names are not allowed
>>> setitem(container, container.__setitem__, u'', item)
Traceback (most recent call last):
...
ValueError: empty names are not allowed
"""
# Do basic name check:
if
isinstance
(
name
,
str
):
try
:
name
=
unicode
(
name
)
except
UnicodeError
:
raise
TypeError
(
"name not unicode or ascii string"
)
elif
not
isinstance
(
name
,
unicode
):
raise
TypeError
(
"name not unicode or ascii string"
)
if
not
name
:
raise
ValueError
(
"empty names are not allowed"
)
old
=
container
.
get
(
name
)
if
old
is
object
:
return
if
old
is
not
None
:
raise
DuplicationError
(
name
)
object
,
event
=
containedEvent
(
object
,
container
,
name
)
setitemf
(
name
,
object
)
if
event
:
notify
(
event
)
notifyContainerModified
(
container
)
fixing_up
=
False
def
uncontained
(
object
,
container
,
name
=
None
):
"""Clear the containment relationship between the `object` and
the `container`.
If we run this using the testing framework, we'll use `getEvents` to
track the events generated:
>>> from zope.component.eventtesting import getEvents
>>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent
>>> from zope.app.container.interfaces import IObjectRemovedEvent
We'll start by creating a container with an item:
>>> class Item(Contained):
... pass
>>> item = Item()
>>> container = {u'foo': item}
>>> x, event = containedEvent(item, container, u'foo')
>>> item.__parent__ is container
1
>>> item.__name__
u'foo'
Now we'll remove the item. It's parent and name are cleared:
>>> uncontained(item, container, u'foo')
>>> item.__parent__
>>> item.__name__
We now have a new removed event:
>>> len(getEvents(IObjectRemovedEvent))
1
>>> event = getEvents(IObjectRemovedEvent)[-1]
>>> event.object is item
1
>>> event.oldParent is container
1
>>> event.oldName
u'foo'
>>> event.newParent
>>> event.newName
As well as a modification event for the container:
>>> len(getEvents(IObjectModifiedEvent))
1
>>> getEvents(IObjectModifiedEvent)[-1].object is container
1
Now if we call uncontained again:
>>> uncontained(item, container, u'foo')
We won't get any new events, because __parent__ and __name__ are None:
>>> len(getEvents(IObjectRemovedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
1
But, if either the name or parent are not ``None`` and they are not the
container and the old name, we'll get a modified event but not a removed
event.
>>> item.__parent__, item.__name__ = container, None
>>> uncontained(item, container, u'foo')
>>> len(getEvents(IObjectRemovedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
2
>>> item.__parent__, item.__name__ = None, u'bar'
>>> uncontained(item, container, u'foo')
>>> len(getEvents(IObjectRemovedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
3
"""
try
:
oldparent
=
object
.
__parent__
oldname
=
object
.
__name__
except
AttributeError
:
# The old object doesn't implements IContained
# Maybe we're converting old data:
if
not
fixing_up
:
raise
oldparent
=
None
oldname
=
None
if
oldparent
is
not
container
or
oldname
!=
name
:
if
oldparent
is
not
None
or
oldname
is
not
None
:
notifyContainerModified
(
container
)
return
event
=
ObjectRemovedEvent
(
object
,
oldparent
,
oldname
)
notify
(
event
)
if
not
IBroken
.
providedBy
(
object
):
object
.
__parent__
=
None
object
.
__name__
=
None
notifyContainerModified
(
container
)
class
NameChooser
(
object
):
zope
.
interface
.
implements
(
INameChooser
)
def
__init__
(
self
,
context
):
self
.
context
=
context
def
checkName
(
self
,
name
,
object
):
"See zope.app.container.interfaces.INameChooser"
if
not
name
:
raise
UserError
(
_
(
"An empty name was provided. Names cannot be empty."
)
)
if
isinstance
(
name
,
str
):
name
=
unicode
(
name
)
elif
not
isinstance
(
name
,
unicode
):
raise
TypeError
(
"Invalid name type"
,
type
(
name
))
if
name
[:
1
]
in
'+@'
or
'/'
in
name
:
raise
UserError
(
_
(
"Names cannot begin with '+' or '@' or contain '/'"
)
)
if
name
in
self
.
context
:
raise
UserError
(
_
(
"The given name is already being used"
)
)
return
True
def
chooseName
(
self
,
name
,
object
):
"See zope.app.container.interfaces.INameChooser"
container
=
self
.
context
if
not
name
:
name
=
object
.
__class__
.
__name__
dot
=
name
.
rfind
(
'.'
)
if
dot
>=
0
:
suffix
=
name
[
dot
:]
name
=
name
[:
dot
]
else
:
suffix
=
''
n
=
name
+
suffix
i
=
1
while
n
in
container
:
i
+=
1
n
=
name
+
u'-'
+
unicode
(
i
)
+
suffix
# Make sure the name is valid. We may have started with something bad.
self
.
checkName
(
n
,
object
)
return
n
class
DecoratorSpecificationDescriptor
(
zope
.
interface
.
declarations
.
ObjectSpecificationDescriptor
):
"""Support for interface declarations on decorators
>>> from zope.interface import *
>>> class I1(Interface):
... pass
>>> class I2(Interface):
... pass
>>> class I3(Interface):
... pass
>>> class I4(Interface):
... pass
>>> class D1(ContainedProxy):
... implements(I1)
>>> class D2(ContainedProxy):
... implements(I2)
>>> class X:
... implements(I3)
>>> x = X()
>>> directlyProvides(x, I4)
Interfaces of X are ordered with the directly-provided interfaces first
>>> [interface.getName() for interface in list(providedBy(x))]
['I4', 'I3']
When we decorate objects, what order should the interfaces come in? One
could argue that decorators are less specific, so they should come last.
>>> [interface.getName() for interface in list(providedBy(D1(x)))]
['I4', 'I3', 'I1', 'IContained', 'IPersistent']
>>> [interface.getName() for interface in list(providedBy(D2(D1(x))))]
['I4', 'I3', 'I1', 'IContained', 'IPersistent', 'I2']
"""
def
__get__
(
self
,
inst
,
cls
=
None
):
if
inst
is
None
:
return
getObjectSpecification
(
cls
)
else
:
provided
=
providedBy
(
getProxiedObject
(
inst
))
# Use type rather than __class__ because inst is a proxy and
# will return the proxied object's class.
cls
=
type
(
inst
)
return
ObjectSpecification
(
provided
,
cls
)
class
DecoratedSecurityCheckerDescriptor
(
object
):
"""Descriptor for a Decorator that provides a decorated security checker.
"""
def
__get__
(
self
,
inst
,
cls
=
None
):
if
inst
is
None
:
return
self
else
:
proxied_object
=
getProxiedObject
(
inst
)
checker
=
getattr
(
proxied_object
,
'__Security_checker__'
,
None
)
if
checker
is
None
:
checker
=
selectChecker
(
proxied_object
)
wrapper_checker
=
selectChecker
(
inst
)
if
wrapper_checker
is
None
:
return
checker
elif
checker
is
None
:
return
wrapper_checker
else
:
return
CombinedChecker
(
wrapper_checker
,
checker
)
class
ContainedProxyClassProvides
(
zope
.
interface
.
declarations
.
ClassProvides
):
def
__set__
(
self
,
inst
,
value
):
inst
=
getProxiedObject
(
inst
)
inst
.
__provides__
=
value
def
__delete__
(
self
,
inst
):
inst
=
getProxiedObject
(
inst
)
del
inst
.
__provides__
class
ContainedProxy
(
ContainedProxyBase
):
# Prevent proxies from having their own instance dictionaries:
__slots__
=
()
__safe_for_unpickling__
=
True
zope
.
interface
.
implements
(
IContained
)
__providedBy__
=
DecoratorSpecificationDescriptor
()
__Security_checker__
=
DecoratedSecurityCheckerDescriptor
()
ContainedProxy
.
__provides__
=
ContainedProxyClassProvides
(
ContainedProxy
,
type
)
src/zope/app/container/dependency.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
"""Subscriber function checking dependencies if a removal is performed
on an object having dependencies. It raises an exception if it's the
case.
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.app
import
zapi
from
zope.i18nmessageid
import
Message
from
zope.app.container.i18n
import
ZopeMessageFactory
as
_
from
zope.app.dependable.interfaces
import
IDependable
,
DependencyError
exception_msg
=
_
(
"""
Removal of object (${object}) which has dependents (${dependents})
is not possible !
You must deactivate this object before trying to remove it.
"""
)
def
CheckDependency
(
event
):
object
=
event
.
object
dependency
=
IDependable
(
object
,
None
)
if
dependency
is
not
None
:
dependents
=
dependency
.
dependents
()
if
dependents
:
mapping
=
{
"object"
:
zapi
.
getPath
(
object
),
"dependents"
:
", "
.
join
(
dependents
)
}
raise
DependencyError
(
Message
(
exception_msg
,
mapping
=
mapping
))
src/zope/app/container/directory.py
0 → 100644
View file @
160df616
##############################################################################
# Copyright (c) 2003 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.
##############################################################################
"""File-system representation adapters for containers
This module includes two adapters (adapter factories, really) for
providing a file-system representation for containers:
`noop`
Factory that "adapts" `IContainer` to `IWriteDirectory`.
This is a lie, since it just returns the original object.
`Cloner`
An `IDirectoryFactory` adapter that just clones the original object.
$Id$
"""
__docformat__
=
'restructuredtext'
import
zope.filerepresentation.interfaces
from
zope.security.proxy
import
removeSecurityProxy
from
zope.interface
import
implements
def
noop
(
container
):
"""Adapt an `IContainer` to an `IWriteDirectory` by just returning it
This "works" because `IContainer` and `IWriteDirectory` have the same
methods, however, the output doesn't actually implement `IWriteDirectory`.
"""
return
container
class
Cloner
(
object
):
"""`IContainer` to `IDirectoryFactory` adapter that clones
This adapter provides a factory that creates a new empty container
of the same class as it's context.
"""
implements
(
zope
.
filerepresentation
.
interfaces
.
IDirectoryFactory
)
def
__init__
(
self
,
context
):
self
.
context
=
context
def
__call__
(
self
,
name
):
# We remove the security proxy so we can actually call the
# class and return an unproxied new object. (We can't use a
# trusted adapter, because the result must be unproxied.) By
# registering this adapter, one effectively gives permission
# to clone the class. Don't use this for classes that have
# exciting side effects as a result of instantiation. :)
return
removeSecurityProxy
(
self
.
context
).
__class__
()
src/zope/app/container/find.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Find Support
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.interface
import
implements
from
interfaces
import
IFind
,
IIdFindFilter
,
IObjectFindFilter
from
interfaces
import
IReadContainer
class
FindAdapter
(
object
):
implements
(
IFind
)
__used_for__
=
IReadContainer
def
__init__
(
self
,
context
):
self
.
_context
=
context
def
find
(
self
,
id_filters
=
None
,
object_filters
=
None
):
'See IFind'
id_filters
=
id_filters
or
[]
object_filters
=
object_filters
or
[]
result
=
[]
container
=
self
.
_context
for
id
,
object
in
container
.
items
():
_find_helper
(
id
,
object
,
container
,
id_filters
,
object_filters
,
result
)
return
result
def
_find_helper
(
id
,
object
,
container
,
id_filters
,
object_filters
,
result
):
for
id_filter
in
id_filters
:
if
not
id_filter
.
matches
(
id
):
break
else
:
# if we didn't break out of the loop, all name filters matched
# now check all object filters
for
object_filter
in
object_filters
:
if
not
object_filter
.
matches
(
object
):
break
else
:
# if we didn't break out of the loop, all filters matched
result
.
append
(
object
)
if
not
IReadContainer
.
providedBy
(
object
):
return
container
=
object
for
id
,
object
in
container
.
items
():
_find_helper
(
id
,
object
,
container
,
id_filters
,
object_filters
,
result
)
class
SimpleIdFindFilter
(
object
):
implements
(
IIdFindFilter
)
def
__init__
(
self
,
ids
):
self
.
_ids
=
ids
def
matches
(
self
,
id
):
'See INameFindFilter'
return
id
in
self
.
_ids
class
SimpleInterfacesFindFilter
(
object
):
"""Filter objects on the provided interfaces"""
implements
(
IObjectFindFilter
)
def
__init__
(
self
,
*
interfaces
):
self
.
interfaces
=
interfaces
def
matches
(
self
,
object
):
for
iface
in
self
.
interfaces
:
if
iface
.
providedBy
(
object
):
return
True
return
False
src/zope/app/container/ftesting.zcml
0 → 100644
View file @
160df616
<configure
xmlns="http://namespaces.zope.org/zope"
i18n_domain="zope"
package="zope.app.container"
>
<!-- This file is the equivalent of site.zcml and it is -->
<!-- used for functional testing setup -->
<include package="zope.app.zcmlfiles" />
<include package="zope.app.container.browser.ftests" />
<include package="zope.app.file"/>
<include package="zope.app.authentication" />
<include package="zope.app.securitypolicy" file="meta.zcml" />
<include package="zope.app.securitypolicy.tests" file="functional.zcml" />
<include package="zope.app.securitypolicy" />
<securityPolicy
component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
<role id="zope.Anonymous" title="Everybody"
description="All users have this role implicitly" />
<role id="zope.Manager" title="Site Manager" />
<!-- Replace the following directive if you don't want public access -->
<grant permission="zope.View"
role="zope.Anonymous" />
<grant permission="zope.app.dublincore.view"
role="zope.Anonymous" />
<grantAll role="zope.Manager" />
<!-- Principals -->
<unauthenticatedPrincipal
id="zope.anybody"
title="Unauthenticated User" />
<!-- Principal that tests generally run as -->
<principal
id="zope.mgr"
title="Manager"
login="mgr"
password="mgrpw" />
<!-- Bootstrap principal used to make local grant to the principal above -->
<principal
id="zope.globalmgr"
title="Manager"
login="globalmgr"
password="globalmgrpw" />
<grant role="zope.Manager" principal="zope.globalmgr" />
</configure>
src/zope/app/container/ftests/__init__.py
0 → 100644
View file @
160df616
#
# This file is necessary to make this directory a package.
src/zope/app/container/ftests/ftesting.zcml
0 → 100644
View file @
160df616
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta"
i18n_domain="zope"
>
<!-- This file is the equivalent of site.zcml and it is -->
<!-- used for functional testing setup -->
<include package="zope.app.zcmlfiles" />
<include package="zope.app.container.browser.ftests" />
<include package="zope.app.file"/>
<include package="zope.app.authentication" />
<include package="zope.app.securitypolicy" file="meta.zcml" />
<include package="zope.app.securitypolicy.tests" file="functional.zcml" />
<include package="zope.app.securitypolicy" />
<securityPolicy
component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
<role id="zope.Anonymous" title="Everybody"
description="All users have this role implicitly" />
<role id="zope.Manager" title="Site Manager" />
<!-- Replace the following directive if you don't want public access -->
<grant permission="zope.View"
role="zope.Anonymous" />
<grant permission="zope.app.dublincore.view"
role="zope.Anonymous" />
<grantAll role="zope.Manager" />
<!-- Principals -->
<unauthenticatedPrincipal
id="zope.anybody"
title="Unauthenticated User" />
<!-- Principal that tests generally run as -->
<principal
id="zope.mgr"
title="Manager"
login="mgr"
password="mgrpw" />
<!-- Bootstrap principal used to make local grant to the principal above -->
<principal
id="zope.globalmgr"
title="Manager"
login="globalmgr"
password="globalmgrpw" />
<grant role="zope.Manager" principal="zope.globalmgr" />
</configure>
src/zope/app/container/ftests/test_view_permissions.py
0 → 100644
View file @
160df616
##############################################################################
#
# 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.
#
##############################################################################
"""Container View Permissions Tests
$Id$
"""
import
unittest
import
transaction
from
zope.security.interfaces
import
Unauthorized
from
zope.app.testing.functional
import
BrowserTestCase
from
zope.app.file
import
File
from
zope.dublincore.interfaces
import
IZopeDublinCore
from
zope.app.securitypolicy.interfaces
import
IRolePermissionManager
from
zope.app.container.testing
import
AppContainerLayer
class
Tests
(
BrowserTestCase
):
def
test_default_view_permissions
(
self
):
"""Tests the default view permissions.
See zope/app/securitypolicy/configure.zcml for the grants of
zope.View and zope.app.dublincore.view to zope.Anonymous. These
ensure that, by default, anonymous users can view container contents.
"""
# add an item that can be viewed from the root folder
file
=
File
()
self
.
getRootFolder
()[
'file'
]
=
file
IZopeDublinCore
(
file
).
title
=
u'My File'
transaction
.
commit
()
response
=
self
.
publish
(
'/'
)
self
.
assertEquals
(
response
.
getStatus
(),
200
)
body
=
response
.
getBody
()
# confirm we can see the file name
self
.
assert_
(
body
.
find
(
'<a href="file">file</a>'
)
!=
-
1
)
# confirm we can see the metadata title
self
.
assert_
(
body
.
find
(
'<td><span>My File</span></td>'
)
!=
-
1
)
def
test_deny_view
(
self
):
"""Tests the denial of view permissions to anonymous.
This test uses the ZMI interface to deny anonymous zope.View permission
to the root folder.
"""
# deny zope.View to zope.Anonymous
prm
=
IRolePermissionManager
(
self
.
getRootFolder
())
prm
.
denyPermissionToRole
(
'zope.View'
,
'zope.Anonymous'
)
transaction
.
commit
()
# confirm Unauthorized when viewing root folder
self
.
assertRaises
(
Unauthorized
,
self
.
publish
,
'/'
)
def
test_deny_dublincore_view
(
self
):
"""Tests the denial of dublincore view permissions to anonymous.
Users who can view a folder contents page but cannot view dublin core
should still be able to see the folder items' names, but not their
title, modified, and created info.
"""
# add an item that can be viewed from the root folder
file
=
File
()
self
.
getRootFolder
()[
'file'
]
=
file
IZopeDublinCore
(
file
).
title
=
u'My File'
# deny zope.app.dublincore.view to zope.Anonymous
prm
=
IRolePermissionManager
(
self
.
getRootFolder
())
prm
.
denyPermissionToRole
(
'zope.app.dublincore.view'
,
'zope.Anonymous'
)
transaction
.
commit
()
response
=
self
.
publish
(
'/'
)
self
.
assertEquals
(
response
.
getStatus
(),
200
)
body
=
response
.
getBody
()
# confirm we can see the file name
self
.
assert_
(
body
.
find
(
'<a href="file">file</a>'
)
!=
-
1
)
# confirm we *cannot* see the metadata title
self
.
assert_
(
body
.
find
(
'My File'
)
==
-
1
)
def
test_suite
():
suite
=
unittest
.
TestSuite
()
Tests
.
layer
=
AppContainerLayer
suite
.
addTest
(
unittest
.
makeSuite
(
Tests
))
return
suite
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/i18n.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Customization of zope.i18n for the Zope application server
$Id$
"""
__docformat__
=
'restructuredtext'
# import this as _ to create i18n messages in the zope domain
from
zope.i18nmessageid
import
MessageFactory
ZopeMessageFactory
=
MessageFactory
(
'zope'
)
src/zope/app/container/interfaces.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Container-related interfaces
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.deprecation
import
deprecated
from
zope.interface
import
Interface
,
Attribute
,
Invalid
from
zope.component.interfaces
import
IView
,
IObjectEvent
from
zope.interface.common.mapping
import
IItemMapping
from
zope.interface.common.mapping
import
IReadMapping
,
IEnumerableMapping
from
zope.location.interfaces
import
ILocation
from
zope.lifecycleevent.interfaces
import
IObjectModifiedEvent
deprecated
(
'IContentContainer'
,
'This interface has been deprecated. '
'Check the "containerViews" zcml directive. '
'The reference will be gone in 3.3'
)
class
DuplicateIDError
(
KeyError
):
pass
class
ContainerError
(
Exception
):
"""An error of a container with one of its components."""
class
InvalidContainerType
(
Invalid
,
TypeError
):
"""The type of a container is not valid."""
class
InvalidItemType
(
Invalid
,
TypeError
):
"""The type of an item is not valid."""
class
InvalidType
(
Invalid
,
TypeError
):
"""The type of an object is not valid."""
class
IContained
(
ILocation
):
"""Objects contained in containers."""
class
IItemContainer
(
IItemMapping
):
"""Minimal readable container."""
class
ISimpleReadContainer
(
IItemContainer
,
IReadMapping
):
"""Readable content containers."""
class
IReadContainer
(
ISimpleReadContainer
,
IEnumerableMapping
):
"""Readable containers that can be enumerated."""
class
IWriteContainer
(
Interface
):
"""An interface for the write aspects of a container."""
def
__setitem__
(
name
,
object
):
"""Add the given `object` to the container under the given name.
Raises a ``TypeError`` if the key is not a unicode or ascii string.
Raises a ``ValueError`` if key is empty.
The container might choose to add a different object than the
one passed to this method.
If the object doesn't implement `IContained`, then one of two
things must be done:
1. If the object implements `ILocation`, then the `IContained`
interface must be declared for the object.
2. Otherwise, a `ContainedProxy` is created for the object and
stored.
The object's `__parent__` and `__name__` attributes are set to the
container and the given name.
If the old parent was ``None``, then an `IObjectAddedEvent` is
generated, otherwise, an `IObjectMovedEvent` is generated. An
`IContainerModifiedEvent` is generated for the container.
If the object replaces another object, then the old object is
deleted before the new object is added, unless the container
vetos the replacement by raising an exception.
If the object's `__parent__` and `__name__` were already set to
the container and the name, then no events are generated and
no hooks. This allows advanced clients to take over event
generation.
"""
def
__delitem__
(
name
):
"""Delete the named object from the container.
Raises a ``KeyError`` if the object is not found.
If the deleted object's `__parent__` and `__name__` match the
container and given name, then an `IObjectRemovedEvent` is
generated and the attributes are set to ``None``. If the object
can be adapted to `IObjectMovedEvent`, then the adapter's
`moveNotify` method is called with the event.
Unless the object's `__parent__` and `__name__` attributes were
initially ``None``, generate an `IContainerModifiedEvent` for the
container.
If the object's `__parent__` and `__name__` were already set to
``None``, then no events are generated. This allows advanced
clients to take over event generation.
"""
class
IItemWriteContainer
(
IWriteContainer
,
IItemContainer
):
"""A write container that also supports minimal reads."""
class
IContentContainer
(
IWriteContainer
):
"""Containers (like folders) that contain ordinary content."""
class
IContainer
(
IReadContainer
,
IWriteContainer
):
"""Readable and writable content container."""
class
IOrderedContainer
(
IContainer
):
"""Containers whose contents are maintained in order."""
def
updateOrder
(
order
):
"""Revise the order of keys, replacing the current ordering.
order is a list or a tuple containing the set of existing keys in
the new order. `order` must contain ``len(keys())`` items and cannot
contain duplicate keys.
Raises ``TypeError`` if order is not a tuple or a list.
Raises ``ValueError`` if order contains an invalid set of keys.
"""
class
IContainerNamesContainer
(
IContainer
):
"""Containers that always choose names for their items."""
##############################################################################
# Moving Objects
class
IObjectMovedEvent
(
IObjectEvent
):
"""An object has been moved."""
oldParent
=
Attribute
(
"The old location parent for the object."
)
oldName
=
Attribute
(
"The old location name for the object."
)
newParent
=
Attribute
(
"The new location parent for the object."
)
newName
=
Attribute
(
"The new location name for the object."
)
##############################################################################
# Adding objects
class
UnaddableError
(
ContainerError
):
"""An object cannot be added to a container."""
def
__init__
(
self
,
container
,
obj
,
message
=
""
):
self
.
container
=
container
self
.
obj
=
obj
self
.
message
=
message
and
": %s"
%
message
def
__str__
(
self
):
return
(
"%(obj)s cannot be added "
"to %(container)s%(message)s"
%
self
.
__dict__
)
class
IObjectAddedEvent
(
IObjectMovedEvent
):
"""An object has been added to a container."""
class
IAdding
(
IView
):
def
add
(
content
):
"""Add content object to container.
Add using the name in `contentName`. Returns the added object
in the context of its container.
If `contentName` is already used in container, raises
``DuplicateIDError``.
"""
contentName
=
Attribute
(
"""The content name, as usually set by the Adder traverser.
If the content name hasn't been defined yet, returns ``None``.
Some creation views might use this to optionally display the
name on forms.
"""
)
def
nextURL
():
"""Return the URL that the creation view should redirect to.
This is called by the creation view after calling add.
It is the adder's responsibility, not the creation view's to
decide what page to display after content is added.
"""
def
nameAllowed
():
"""Return whether names can be input by the user."""
def
addingInfo
():
"""Return add menu data as a sequence of mappings.
Each mapping contains 'action', 'title', and possibly other keys.
The result is sorted by title.
"""
def
isSingleMenuItem
():
"""Return whether there is single menu item or not."""
def
hasCustomAddView
():
"This should be called only if there is `singleMenuItem` else return 0"
class
INameChooser
(
Interface
):
def
checkName
(
name
,
object
):
"""Check whether an object name is valid.
Raises a user error if the name is not valid.
"""
def
chooseName
(
name
,
object
):
"""Choose a unique valid name for the object
The given name and object may be taken into account when
choosing the name.
"""
##############################################################################
# Removing objects
class
IObjectRemovedEvent
(
IObjectMovedEvent
):
"""An object has been removed from a container."""
##############################################################################
# Modifying containers
class
IContainerModifiedEvent
(
IObjectModifiedEvent
):
"""The container has been modified.
This event is specific to "containerness" modifications, which means
addition, removal or reordering of sub-objects.
"""
##############################################################################
# Finding objects
class
IFind
(
Interface
):
"""
Find support for containers.
"""
def
find
(
id_filters
=
None
,
object_filters
=
None
):
"""Find object that matches all filters in all sub-objects.
This container itself is not included.
"""
class
IObjectFindFilter
(
Interface
):
def
matches
(
object
):
"""Return True if the object matches the filter criteria."""
class
IIdFindFilter
(
Interface
):
def
matches
(
id
):
"""Return True if the id matches the filter criteria."""
src/zope/app/container/ordered.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Ordered container implementation.
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.app.container.interfaces
import
IOrderedContainer
from
zope.interface
import
implements
from
persistent
import
Persistent
from
persistent.dict
import
PersistentDict
from
persistent.list
import
PersistentList
from
types
import
StringTypes
,
TupleType
,
ListType
from
zope.app.container.contained
import
Contained
,
setitem
,
uncontained
from
zope.app.container.contained
import
notifyContainerModified
class
OrderedContainer
(
Persistent
,
Contained
):
""" `OrderedContainer` maintains entries' order as added and moved.
>>> oc = OrderedContainer()
>>> int(IOrderedContainer.providedBy(oc))
1
>>> len(oc)
0
"""
implements
(
IOrderedContainer
)
def
__init__
(
self
):
self
.
_data
=
PersistentDict
()
self
.
_order
=
PersistentList
()
def
keys
(
self
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc.keys()
['foo']
>>> oc['baz'] = 'quux'
>>> oc.keys()
['foo', 'baz']
>>> int(len(oc._order) == len(oc._data))
1
"""
return
self
.
_order
[:]
def
__iter__
(
self
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc['baz'] = 'quux'
>>> [i for i in oc]
['foo', 'baz']
>>> int(len(oc._order) == len(oc._data))
1
"""
return
iter
(
self
.
keys
())
def
__getitem__
(
self
,
key
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> oc['foo']
'bar'
"""
return
self
.
_data
[
key
]
def
get
(
self
,
key
,
default
=
None
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> oc.get('foo')
'bar'
>>> oc.get('funky', 'No chance, dude.')
'No chance, dude.'
"""
return
self
.
_data
.
get
(
key
,
default
)
def
values
(
self
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc.values()
['bar']
>>> oc['baz'] = 'quux'
>>> oc.values()
['bar', 'quux']
>>> int(len(oc._order) == len(oc._data))
1
"""
return
[
self
.
_data
[
i
]
for
i
in
self
.
_order
]
def
__len__
(
self
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> int(len(oc) == 0)
1
>>> oc['foo'] = 'bar'
>>> int(len(oc) == 1)
1
"""
return
len
(
self
.
_data
)
def
items
(
self
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc.items()
[('foo', 'bar')]
>>> oc['baz'] = 'quux'
>>> oc.items()
[('foo', 'bar'), ('baz', 'quux')]
>>> int(len(oc._order) == len(oc._data))
1
"""
return
[(
i
,
self
.
_data
[
i
])
for
i
in
self
.
_order
]
def
__contains__
(
self
,
key
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> int('foo' in oc)
1
>>> int('quux' in oc)
0
"""
return
self
.
_data
.
has_key
(
key
)
has_key
=
__contains__
def
__setitem__
(
self
,
key
,
object
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc._order
['foo']
>>> oc['baz'] = 'quux'
>>> oc._order
['foo', 'baz']
>>> int(len(oc._order) == len(oc._data))
1
"""
existed
=
self
.
_data
.
has_key
(
key
)
bad
=
False
if
isinstance
(
key
,
StringTypes
):
try
:
unicode
(
key
)
except
UnicodeError
:
bad
=
True
else
:
bad
=
True
if
bad
:
raise
TypeError
(
"'%s' is invalid, the key must be an "
"ascii or unicode string"
%
key
)
if
len
(
key
)
==
0
:
raise
ValueError
(
"The key cannot be an empty string"
)
# We have to first update the order, so that the item is available,
# otherwise most API functions will lie about their available values
# when an event subscriber tries to do something with the container.
if
not
existed
:
self
.
_order
.
append
(
key
)
# This function creates a lot of events that other code listens to.
setitem
(
self
,
self
.
_data
.
__setitem__
,
key
,
object
)
return
key
def
__delitem__
(
self
,
key
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc['baz'] = 'quux'
>>> oc['zork'] = 'grue'
>>> oc.items()
[('foo', 'bar'), ('baz', 'quux'), ('zork', 'grue')]
>>> int(len(oc._order) == len(oc._data))
1
>>> del oc['baz']
>>> oc.items()
[('foo', 'bar'), ('zork', 'grue')]
>>> int(len(oc._order) == len(oc._data))
1
"""
uncontained
(
self
.
_data
[
key
],
self
,
key
)
del
self
.
_data
[
key
]
self
.
_order
.
remove
(
key
)
def
updateOrder
(
self
,
order
):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> oc['baz'] = 'quux'
>>> oc['zork'] = 'grue'
>>> oc.keys()
['foo', 'baz', 'zork']
>>> oc.updateOrder(['baz', 'foo', 'zork'])
>>> oc.keys()
['baz', 'foo', 'zork']
>>> oc.updateOrder(['baz', 'zork', 'foo'])
>>> oc.keys()
['baz', 'zork', 'foo']
>>> oc.updateOrder(['baz', 'zork', 'foo'])
>>> oc.keys()
['baz', 'zork', 'foo']
>>> oc.updateOrder(('zork', 'foo', 'baz'))
>>> oc.keys()
['zork', 'foo', 'baz']
>>> oc.updateOrder(['baz', 'zork'])
Traceback (most recent call last):
...
ValueError: Incompatible key set.
>>> oc.updateOrder(['foo', 'bar', 'baz', 'quux'])
Traceback (most recent call last):
...
ValueError: Incompatible key set.
>>> oc.updateOrder(1)
Traceback (most recent call last):
...
TypeError: order must be a tuple or a list.
>>> oc.updateOrder('bar')
Traceback (most recent call last):
...
TypeError: order must be a tuple or a list.
>>> oc.updateOrder(['baz', 'zork', 'quux'])
Traceback (most recent call last):
...
ValueError: Incompatible key set.
>>> del oc['baz']
>>> del oc['zork']
>>> del oc['foo']
>>> len(oc)
0
"""
if
not
isinstance
(
order
,
ListType
)
and
\
not
isinstance
(
order
,
TupleType
):
raise
TypeError
(
'order must be a tuple or a list.'
)
if
len
(
order
)
!=
len
(
self
.
_order
):
raise
ValueError
(
"Incompatible key set."
)
was_dict
=
{}
will_be_dict
=
{}
new_order
=
PersistentList
()
for
i
in
range
(
len
(
order
)):
was_dict
[
self
.
_order
[
i
]]
=
1
will_be_dict
[
order
[
i
]]
=
1
new_order
.
append
(
order
[
i
])
if
will_be_dict
!=
was_dict
:
raise
ValueError
(
"Incompatible key set."
)
self
.
_order
=
new_order
notifyContainerModified
(
self
)
src/zope/app/container/sample.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Sample container implementation.
This is primarily for testing purposes.
It might be useful as a mix-in for some classes, but many classes will
need a very different implementation.
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.app.container.interfaces
import
IContainer
from
zope.interface
import
implements
from
zope.app.container.contained
import
Contained
,
setitem
,
uncontained
class
SampleContainer
(
Contained
):
"""Sample container implementation suitable for testing.
It is not suitable, directly as a base class unless the subclass
overrides `_newContainerData` to return a persistent mapping object.
"""
implements
(
IContainer
)
def
__init__
(
self
):
self
.
__data
=
self
.
_newContainerData
()
def
_newContainerData
(
self
):
"""Construct an item-data container
Subclasses should override this if they want different data.
The value returned is a mapping object that also has `get`,
`has_key`, `keys`, `items`, and `values` methods.
"""
return
{}
def
keys
(
self
):
'''See interface `IReadContainer`'''
return
self
.
__data
.
keys
()
def
__iter__
(
self
):
return
iter
(
self
.
__data
)
def
__getitem__
(
self
,
key
):
'''See interface `IReadContainer`'''
return
self
.
__data
[
key
]
def
get
(
self
,
key
,
default
=
None
):
'''See interface `IReadContainer`'''
return
self
.
__data
.
get
(
key
,
default
)
def
values
(
self
):
'''See interface `IReadContainer`'''
return
self
.
__data
.
values
()
def
__len__
(
self
):
'''See interface `IReadContainer`'''
return
len
(
self
.
__data
)
def
items
(
self
):
'''See interface `IReadContainer`'''
return
self
.
__data
.
items
()
def
__contains__
(
self
,
key
):
'''See interface `IReadContainer`'''
return
self
.
__data
.
has_key
(
key
)
has_key
=
__contains__
def
__setitem__
(
self
,
key
,
object
):
'''See interface `IWriteContainer`'''
setitem
(
self
,
self
.
__data
.
__setitem__
,
key
,
object
)
def
__delitem__
(
self
,
key
):
'''See interface `IWriteContainer`'''
uncontained
(
self
.
__data
[
key
],
self
,
key
)
del
self
.
__data
[
key
]
src/zope/app/container/size.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
"""Adapters that give the size of an object.
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.app.container.i18n
import
ZopeMessageFactory
as
_
from
zope.size.interfaces
import
ISized
from
zope.interface
import
implements
class
ContainerSized
(
object
):
implements
(
ISized
)
def
__init__
(
self
,
container
):
self
.
_container
=
container
def
sizeForSorting
(
self
):
"""See `ISized`"""
return
(
'item'
,
len
(
self
.
_container
))
def
sizeForDisplay
(
self
):
"""See `ISized`"""
num_items
=
len
(
self
.
_container
)
if
num_items
==
1
:
return
_
(
'1 item'
)
return
_
(
'${items} items'
,
mapping
=
{
'items'
:
str
(
num_items
)})
src/zope/app/container/testing.py
0 → 100644
View file @
160df616
import
os
from
zope.app.testing.functional
import
ZCMLLayer
AppContainerLayer
=
ZCMLLayer
(
os
.
path
.
join
(
os
.
path
.
split
(
__file__
)[
0
],
'ftesting.zcml'
),
__name__
,
'AppContainerLayer'
,
allow_teardown
=
True
)
src/zope/app/container/tests/__init__.py
0 → 100644
View file @
160df616
#
# This file is necessary to make this directory a package.
src/zope/app/container/tests/placelesssetup.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
"""Unit test logic for setting up and tearing down basic infrastructure
$Id$
"""
from
zope.app.testing
import
ztapi
from
zope.app.container.interfaces
import
IWriteContainer
,
INameChooser
from
zope.app.container.contained
import
NameChooser
class
PlacelessSetup
(
object
):
def
setUp
(
self
):
ztapi
.
provideAdapter
(
IWriteContainer
,
INameChooser
,
NameChooser
)
src/zope/app/container/tests/test_btree.py
0 → 100644
View file @
160df616
##############################################################################
#
# 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.
#
##############################################################################
"""BTree Container Tests
$Id$
"""
from
unittest
import
TestCase
,
main
,
makeSuite
,
TestSuite
from
zope.testing.doctestunit
import
DocTestSuite
from
zope.app.testing
import
placelesssetup
from
test_icontainer
import
TestSampleContainer
class
TestBTreeContainer
(
TestSampleContainer
,
TestCase
):
def
makeTestObject
(
self
):
from
zope.app.container.btree
import
BTreeContainer
return
BTreeContainer
()
def
test_suite
():
return
TestSuite
((
makeSuite
(
TestBTreeContainer
),
DocTestSuite
(
'zope.app.container.btree'
,
setUp
=
placelesssetup
.
setUp
,
tearDown
=
placelesssetup
.
tearDown
),
))
if
__name__
==
'__main__'
:
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/tests/test_constraints.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Container constraint tests
$Id$
"""
import
unittest
from
zope.testing
import
doctest
,
module
def
setUp
(
test
):
module
.
setUp
(
test
,
'zope.app.container.constraints_txt'
)
def
tearDown
(
test
):
module
.
tearDown
(
test
,
'zope.app.container.constraints_txt'
)
def
test_suite
():
return
unittest
.
TestSuite
((
doctest
.
DocTestSuite
(
'zope.app.container.constraints'
),
doctest
.
DocFileSuite
(
'../constraints.txt'
,
setUp
=
setUp
,
tearDown
=
tearDown
),
))
if
__name__
==
'__main__'
:
unittest
.
main
()
src/zope/app/container/tests/test_contained.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Contained Tests
$Id$
"""
import
unittest
import
gc
from
ZODB.DemoStorage
import
DemoStorage
from
ZODB.DB
import
DB
import
transaction
from
persistent
import
Persistent
import
zope.interface
from
zope.testing
import
doctest
from
zope.app.container.contained
import
ContainedProxy
from
zope.app.testing
import
placelesssetup
class
MyOb
(
Persistent
):
pass
def
test_basic_proxy_attribute_management_and_picklability
():
"""Contained-object proxy
This is a picklable proxy that can be put around objects that
don't implement IContained.
>>> l = [1, 2, 3]
>>> p = ContainedProxy(l)
>>> p.__parent__ = 'Dad'
>>> p.__name__ = 'p'
>>> p
[1, 2, 3]
>>> p.__parent__
'Dad'
>>> p.__name__
'p'
>>> import pickle
>>> p2 = pickle.loads(pickle.dumps(p))
>>> p2
[1, 2, 3]
>>> p2.__parent__
'Dad'
>>> p2.__name__
'p'
"""
def
test_basic_persistent_w_non_persistent_proxied
():
"""
>>> p = ContainedProxy([1])
>>> p.__parent__ = 2
>>> p.__name__ = 'test'
>>> db = DB(DemoStorage('test_storage'))
>>> c = db.open()
>>> c.root()['p'] = p
>>> transaction.commit()
>>> c2 = db.open()
>>> p2 = c2.root()['p']
>>> p2
[1]
>>> p2.__parent__
2
>>> p2.__name__
'test'
>>> p2._p_changed
0
>>> p2._p_deactivate()
>>> p2._p_changed
>>> p2.__name__
'test'
>>> db.close()
"""
def
test_declarations_on_ContainedProxy
():
r"""
It is possible to make declarations on ContainedProxy objects.
>>> class I1(zope.interface.Interface):
... pass
>>> class C(object):
... zope.interface.implements(I1)
>>> c = C()
>>> p = ContainedProxy(c)
ContainedProxy provides no interfaces on it's own:
>>> tuple(zope.interface.providedBy(ContainedProxy))
()
It implements IContained and IPersistent:
>>> tuple(zope.interface.implementedBy(ContainedProxy))
(<InterfaceClass zope.app.container.interfaces.IContained>,
<InterfaceClass persistent.interfaces.IPersistent>)
A proxied object has IContainer, in addition to what the unproxied
object has:
>>> tuple(zope.interface.providedBy(p))
(<InterfaceClass zope.app.container.tests.test_contained.I1>,
<InterfaceClass zope.app.container.interfaces.IContained>,
<InterfaceClass persistent.interfaces.IPersistent>)
>>> class I2(zope.interface.Interface):
... pass
>>> zope.interface.directlyProvides(c, I2)
>>> tuple(zope.interface.providedBy(p))
(<InterfaceClass zope.app.container.tests.test_contained.I2>,
<InterfaceClass zope.app.container.tests.test_contained.I1>,
<InterfaceClass zope.app.container.interfaces.IContained>,
<InterfaceClass persistent.interfaces.IPersistent>)
We can declare interfaces through the proxy:
>>> class I3(zope.interface.Interface):
... pass
>>> zope.interface.directlyProvides(p, I3)
>>> tuple(zope.interface.providedBy(p))
(<InterfaceClass zope.app.container.tests.test_contained.I3>,
<InterfaceClass zope.app.container.tests.test_contained.I1>,
<InterfaceClass zope.app.container.interfaces.IContained>,
<InterfaceClass persistent.interfaces.IPersistent>)
"""
def
test_basic_persistent_w_persistent_proxied
():
"""
Here, we'll verify that shared references work and
that updates to both the proxies and the proxied objects
are made correctly.
----------------------
| |
parent other
| /
ob <--------------
Here we have an object, parent, that contains ob. There is another
object, other, that has a non-container reference to ob.
>>> parent = MyOb()
>>> parent.ob = ContainedProxy(MyOb())
>>> parent.ob.__parent__ = parent
>>> parent.ob.__name__ = 'test'
>>> other = MyOb()
>>> other.ob = parent.ob
We can change ob through either parent or other
>>> parent.ob.x = 1
>>> other.ob.y = 2
Now we'll save the data:
>>> db = DB(DemoStorage('test_storage'))
>>> c1 = db.open()
>>> c1.root()['parent'] = parent
>>> c1.root()['other'] = other
>>> transaction.commit()
We'll open a second connection and verify that we have the data we
expect:
>>> c2 = db.open()
>>> p2 = c2.root()['parent']
>>> p2.ob.__parent__ is p2
1
>>> p2.ob.x
1
>>> p2.ob.y
2
>>> o2 = c2.root()['other']
>>> o2.ob is p2.ob
1
>>> o2.ob is p2.ob
1
>>> o2.ob.__name__
'test'
Now we'll change things around a bit. We'll move things around
a bit. We'll also add an attribute to ob
>>> o2.ob.__name__ = 'test 2'
>>> o2.ob.__parent__ = o2
>>> o2.ob.z = 3
>>> p2.ob.__parent__ is p2
0
>>> p2.ob.__parent__ is o2
1
And save the changes:
>>> transaction.commit()
Now we'll reopen the first connection and verify that we can see
the changes:
>>> c1.close()
>>> c1 = db.open()
>>> p2 = c1.root()['parent']
>>> p2.ob.__name__
'test 2'
>>> p2.ob.z
3
>>> p2.ob.__parent__ is c1.root()['other']
1
>>> db.close()
"""
def
test_proxy_cache_interaction
():
"""Test to make sure the proxy properly interacts with the object cache
Persistent objects are their own weak refs. Thier deallocators
need to notify their connection's cache that their object is being
deallocated, so that it is removed from the cache.
>>> from ZODB.tests.util import DB
>>> db = DB()
>>> db.setCacheSize(5)
>>> conn = db.open()
>>> conn.root()['p'] = ContainedProxy(None)
We need to create some filler objects to push our proxy out of the cache:
>>> for i in range(10):
... conn.root()[i] = MyOb()
>>> transaction.commit()
Let's get the oid of our proxy:
>>> oid = conn.root()['p']._p_oid
Now, we'll access the filler object's:
>>> x = [getattr(conn.root()[i], 'x', 0) for i in range(10)]
We've also accessed the root object. If we garbage-collect the
cache:
>>> conn._cache.incrgc()
Then the root object will still be active, because it was accessed
recently:
>>> conn.root()._p_changed
0
And the proxy will be in the cache, because it's refernced from
the root object:
>>> conn._cache.get(oid) is not None
True
But it's a ghost:
>>> conn.root()['p']._p_changed
If we deactivate the root object:
>>> conn.root()._p_deactivate()
Then we'll release the last reference to the proxy and it should
no longer be in the cache. To be sure, we'll call gc:
>>> x = gc.collect()
>>> conn._cache.get(oid) is not None
False
"""
def
test_ContainedProxy_instances_have_no_instance_dictionaries
():
"""Make sure that proxies don't introduce extra instance dictionaries
>>> from zope.app.container.contained import ContainedProxy
>>> class C:
... pass
>>> c = C()
>>> c.x = 1
>>> c.__dict__
{'x': 1}
>>> p = ContainedProxy(c)
>>> p.__dict__
{'x': 1}
>>> p.y = 3
>>> p.__dict__
{'y': 3, 'x': 1}
>>> c.__dict__
{'y': 3, 'x': 1}
>>> p.__dict__ is c.__dict__
True
"""
def
test_suite
():
return
unittest
.
TestSuite
((
doctest
.
DocTestSuite
(
'zope.app.container.contained'
,
setUp
=
placelesssetup
.
setUp
,
tearDown
=
placelesssetup
.
tearDown
),
doctest
.
DocTestSuite
(
optionflags
=
doctest
.
NORMALIZE_WHITESPACE
),
))
if
__name__
==
'__main__'
:
unittest
.
main
()
src/zope/app/container/tests/test_containertraversable.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Container Traverser tests.
$Id$
"""
import
unittest
from
zope.testing.cleanup
import
CleanUp
from
zope.interface
import
implements
from
zope.traversing.interfaces
import
TraversalError
from
zope.app.container.traversal
import
ContainerTraversable
from
zope.app.container.interfaces
import
IContainer
class
Container
(
object
):
implements
(
IContainer
)
def
__init__
(
self
,
attrs
=
{},
objs
=
{}):
for
attr
,
value
in
attrs
.
iteritems
():
setattr
(
self
,
attr
,
value
)
self
.
__objs
=
{}
for
name
,
value
in
objs
.
iteritems
():
self
.
__objs
[
name
]
=
value
def
__getitem__
(
self
,
name
):
return
self
.
__objs
[
name
]
def
get
(
self
,
name
,
default
=
None
):
return
self
.
__objs
.
get
(
name
,
default
)
def
__contains__
(
self
,
name
):
return
self
.
__objs
.
has_key
(
name
)
class
Test
(
CleanUp
,
unittest
.
TestCase
):
def
testAttr
(
self
):
# test container path traversal
foo
=
Container
()
bar
=
Container
()
baz
=
Container
()
c
=
Container
({
'foo'
:
foo
},
{
'bar'
:
bar
,
'foo'
:
baz
})
T
=
ContainerTraversable
(
c
)
self
.
failUnless
(
T
.
traverse
(
'foo'
,
[])
is
baz
)
self
.
failUnless
(
T
.
traverse
(
'bar'
,
[])
is
bar
)
self
.
assertRaises
(
TraversalError
,
T
.
traverse
,
'morebar'
,
[])
def
test_suite
():
loader
=
unittest
.
TestLoader
()
return
loader
.
loadTestsFromTestCase
(
Test
)
if
__name__
==
'__main__'
:
unittest
.
TextTestRunner
().
run
(
test_suite
())
src/zope/app/container/tests/test_containertraverser.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Container Traverser Tests
$Id$
"""
import
unittest
from
zope.app.container.traversal
import
ContainerTraverser
from
zope.app.container.interfaces
import
IReadContainer
from
zope.app.testing
import
ztapi
,
placelesssetup
from
zope.publisher.interfaces
import
NotFound
from
zope.publisher.browser
import
TestRequest
from
zope.interface
import
implements
class
TestContainer
(
object
):
implements
(
IReadContainer
)
def
__init__
(
self
,
**
kw
):
for
name
,
value
in
kw
.
items
():
setattr
(
self
,
name
,
value
)
def
get
(
self
,
name
,
default
=
None
):
return
getattr
(
self
,
name
,
default
)
class
View
(
object
):
def
__init__
(
self
,
context
,
request
):
self
.
context
=
context
self
.
request
=
request
class
TraverserTest
(
placelesssetup
.
PlacelessSetup
,
unittest
.
TestCase
):
# The following two methods exist, so that other container traversers can
# use these tests as a base.
def
_getTraverser
(
self
,
context
,
request
):
return
ContainerTraverser
(
context
,
request
)
def
_getContainer
(
self
,
**
kw
):
return
TestContainer
(
**
kw
)
def
setUp
(
self
):
super
(
TraverserTest
,
self
).
setUp
()
# Create a small object tree
self
.
foo
=
self
.
_getContainer
()
foo2
=
self
.
_getContainer
(
Foo
=
self
.
foo
)
# Initiate a request
self
.
request
=
TestRequest
()
# Create the traverser
self
.
traverser
=
self
.
_getTraverser
(
foo2
,
self
.
request
)
# Define a simple view for the container
ztapi
.
browserView
(
IReadContainer
,
'viewfoo'
,
View
)
def
test_itemTraversal
(
self
):
self
.
assertEqual
(
self
.
traverser
.
publishTraverse
(
self
.
request
,
'Foo'
),
self
.
foo
)
self
.
assertRaises
(
NotFound
,
self
.
traverser
.
publishTraverse
,
self
.
request
,
'morebar'
)
def
test_viewTraversal
(
self
):
self
.
assertEquals
(
self
.
traverser
.
publishTraverse
(
self
.
request
,
'viewfoo'
).
__class__
,
View
)
self
.
assertEquals
(
self
.
traverser
.
publishTraverse
(
self
.
request
,
'Foo'
),
self
.
foo
)
self
.
assertRaises
(
NotFound
,
self
.
traverser
.
publishTraverse
,
self
.
request
,
'morebar'
)
self
.
assertRaises
(
NotFound
,
self
.
traverser
.
publishTraverse
,
self
.
request
,
'@@morebar'
)
def
test_suite
():
return
unittest
.
TestSuite
((
unittest
.
makeSuite
(
TraverserTest
),
))
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/tests/test_directory.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""FS-based directory implementation tests for containers
$Id$
"""
from
unittest
import
TestCase
,
TestSuite
,
main
,
makeSuite
import
zope.app.container.directory
class
Directory
(
object
):
pass
class
Test
(
TestCase
):
def
test_Cloner
(
self
):
d
=
Directory
()
d
.
a
=
1
clone
=
zope
.
app
.
container
.
directory
.
Cloner
(
d
)(
'foo'
)
self
.
assert_
(
clone
!=
d
)
self
.
assertEqual
(
clone
.
__class__
,
d
.
__class__
)
def
test_suite
():
return
TestSuite
((
makeSuite
(
Test
),
))
if
__name__
==
'__main__'
:
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/tests/test_find.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Find functionality tests
$Id$
"""
from
unittest
import
TestCase
,
main
,
makeSuite
from
zope.app.container.interfaces
import
IReadContainer
from
zope.app.container.interfaces
import
IObjectFindFilter
from
zope.app.container.find
import
FindAdapter
,
SimpleIdFindFilter
from
zope.app.container.find
import
SimpleInterfacesFindFilter
from
zope.interface
import
implements
,
Interface
,
directlyProvides
class
FakeContainer
(
object
):
implements
(
IReadContainer
)
def
__init__
(
self
,
id
,
objects
):
self
.
_id
=
id
self
.
_objects
=
objects
def
keys
(
self
):
return
[
object
.
_id
for
object
in
self
.
_objects
]
def
values
(
self
):
return
self
.
_objects
def
items
(
self
):
return
[(
object
.
_id
,
object
)
for
object
in
self
.
_objects
]
def
__getitem__
(
self
,
id
):
for
object
in
self
.
_objects
:
if
object
.
_id
==
id
:
return
object
raise
KeyError
(
"Could not find %s"
%
id
)
def
get
(
self
,
id
,
default
=
None
):
for
object
in
self
.
_objects
:
if
object
.
_id
==
id
:
return
object
return
default
def
__contains__
(
self
,
id
):
for
object
in
self
.
_objects
:
if
object
.
id
==
id
:
return
True
return
False
def
__len__
(
self
):
return
len
(
self
.
_objects
)
class
FakeInterfaceFoo
(
Interface
):
"""Test interface Foo"""
class
FakeInterfaceBar
(
Interface
):
"""Test interface Bar"""
class
FakeInterfaceSpam
(
Interface
):
"""Test interface Spam"""
class
TestObjectFindFilter
(
object
):
implements
(
IObjectFindFilter
)
def
__init__
(
self
,
count
):
self
.
_count
=
count
def
matches
(
self
,
object
):
if
IReadContainer
.
providedBy
(
object
):
return
len
(
object
)
==
self
.
_count
else
:
return
False
class
Test
(
TestCase
):
def
test_idFind
(
self
):
alpha
=
FakeContainer
(
'alpha'
,
[])
delta
=
FakeContainer
(
'delta'
,
[])
beta
=
FakeContainer
(
'beta'
,
[
delta
])
gamma
=
FakeContainer
(
'gamma'
,
[])
tree
=
FakeContainer
(
'tree'
,
[
alpha
,
beta
,
gamma
])
find
=
FindAdapter
(
tree
)
# some simple searches
result
=
find
.
find
([
SimpleIdFindFilter
([
'beta'
])])
self
.
assertEquals
([
beta
],
result
)
result
=
find
.
find
([
SimpleIdFindFilter
([
'gamma'
])])
self
.
assertEquals
([
gamma
],
result
)
result
=
find
.
find
([
SimpleIdFindFilter
([
'delta'
])])
self
.
assertEquals
([
delta
],
result
)
# we should not find the container we search on
result
=
find
.
find
([
SimpleIdFindFilter
([
'tree'
])])
self
.
assertEquals
([],
result
)
# search for multiple ids
result
=
find
.
find
([
SimpleIdFindFilter
([
'alpha'
,
'beta'
])])
self
.
assertEquals
([
alpha
,
beta
],
result
)
result
=
find
.
find
([
SimpleIdFindFilter
([
'beta'
,
'delta'
])])
self
.
assertEquals
([
beta
,
delta
],
result
)
# search without any filters, find everything
result
=
find
.
find
([])
self
.
assertEquals
([
alpha
,
beta
,
delta
,
gamma
],
result
)
# search for something that doesn't exist
result
=
find
.
find
([
SimpleIdFindFilter
([
'foo'
])])
self
.
assertEquals
([],
result
)
# find for something that has two ids at the same time,
# can't ever be the case
result
=
find
.
find
([
SimpleIdFindFilter
([
'alpha'
]),
SimpleIdFindFilter
([
'beta'
])])
self
.
assertEquals
([],
result
)
def
test_objectFind
(
self
):
alpha
=
FakeContainer
(
'alpha'
,
[])
delta
=
FakeContainer
(
'delta'
,
[])
beta
=
FakeContainer
(
'beta'
,
[
delta
])
gamma
=
FakeContainer
(
'gamma'
,
[])
tree
=
FakeContainer
(
'tree'
,
[
alpha
,
beta
,
gamma
])
find
=
FindAdapter
(
tree
)
result
=
find
.
find
(
object_filters
=
[
TestObjectFindFilter
(
0
)])
self
.
assertEquals
([
alpha
,
delta
,
gamma
],
result
)
result
=
find
.
find
(
object_filters
=
[
TestObjectFindFilter
(
1
)])
self
.
assertEquals
([
beta
],
result
)
result
=
find
.
find
(
object_filters
=
[
TestObjectFindFilter
(
2
)])
self
.
assertEquals
([],
result
)
def
test_combinedFind
(
self
):
alpha
=
FakeContainer
(
'alpha'
,
[])
delta
=
FakeContainer
(
'delta'
,
[])
beta
=
FakeContainer
(
'beta'
,
[
delta
])
gamma
=
FakeContainer
(
'gamma'
,
[])
tree
=
FakeContainer
(
'tree'
,
[
alpha
,
beta
,
gamma
])
find
=
FindAdapter
(
tree
)
result
=
find
.
find
(
id_filters
=
[
SimpleIdFindFilter
([
'alpha'
])],
object_filters
=
[
TestObjectFindFilter
(
0
)])
self
.
assertEquals
([
alpha
],
result
)
result
=
find
.
find
(
id_filters
=
[
SimpleIdFindFilter
([
'alpha'
])],
object_filters
=
[
TestObjectFindFilter
(
1
)])
self
.
assertEquals
([],
result
)
def
test_interfaceFind
(
self
):
alpha
=
FakeContainer
(
'alpha'
,
[])
directlyProvides
(
alpha
,
FakeInterfaceBar
)
delta
=
FakeContainer
(
'delta'
,
[])
directlyProvides
(
delta
,
FakeInterfaceFoo
)
beta
=
FakeContainer
(
'beta'
,
[
delta
])
directlyProvides
(
beta
,
FakeInterfaceSpam
)
gamma
=
FakeContainer
(
'gamma'
,
[])
tree
=
FakeContainer
(
'tree'
,
[
alpha
,
beta
,
gamma
])
find
=
FindAdapter
(
tree
)
result
=
find
.
find
(
object_filters
=
[
SimpleInterfacesFindFilter
(
FakeInterfaceFoo
,
FakeInterfaceSpam
)])
self
.
assertEqual
([
beta
,
delta
],
result
)
def
test_suite
():
return
makeSuite
(
Test
)
if
__name__
==
'__main__'
:
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/tests/test_icontainer.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Test the IContainer interface.
$Id$
"""
from
unittest
import
TestCase
,
main
,
makeSuite
from
zope.interface.verify
import
verifyObject
from
zope.app.container.interfaces
import
IContainer
from
zope.app.testing
import
placelesssetup
def
DefaultTestData
():
return
[(
'3'
,
'0'
),
(
'2'
,
'1'
),
(
'4'
,
'2'
),
(
'6'
,
'3'
),
(
'0'
,
'4'
),
(
'5'
,
'5'
),
(
'1'
,
'6'
),
(
'8'
,
'7'
),
(
'7'
,
'8'
),
(
'9'
,
'9'
)]
class
BaseTestIContainer
(
placelesssetup
.
PlacelessSetup
):
"""Base test cases for containers.
Subclasses must define a makeTestObject that takes no
arguments and that returns a new empty test container,
and a makeTestData that also takes no arguments and returns
a sequence of (key, value) pairs that may be stored in
the test container. The list must be at least ten items long.
'NoSuchKey' may not be used as a key value in the returned list.
"""
def
__setUp
(
self
):
self
.
__container
=
container
=
self
.
makeTestObject
()
self
.
__data
=
data
=
self
.
makeTestData
()
for
k
,
v
in
data
:
container
[
k
]
=
v
return
container
,
data
############################################################
# Interface-driven tests:
def
testIContainerVerify
(
self
):
verifyObject
(
IContainer
,
self
.
makeTestObject
())
def
test_keys
(
self
):
# See interface IReadContainer
container
=
self
.
makeTestObject
()
keys
=
container
.
keys
()
self
.
assertEqual
(
list
(
keys
),
[])
container
,
data
=
self
.
__setUp
()
keys
=
container
.
keys
()
keys
=
list
(
keys
);
keys
.
sort
()
# convert to sorted list
ikeys
=
[
k
for
k
,
v
in
data
];
ikeys
.
sort
()
# sort input keys
self
.
assertEqual
(
keys
,
ikeys
)
def
test_get
(
self
):
# See interface IReadContainer
default
=
object
()
data
=
self
.
makeTestData
()
container
=
self
.
makeTestObject
()
self
.
assertRaises
(
KeyError
,
container
.
__getitem__
,
data
[
0
][
0
])
self
.
assertEqual
(
container
.
get
(
data
[
0
][
0
],
default
),
default
)
container
,
data
=
self
.
__setUp
()
self
.
assertRaises
(
KeyError
,
container
.
__getitem__
,
self
.
getUnknownKey
())
self
.
assertEqual
(
container
.
get
(
self
.
getUnknownKey
(),
default
),
default
)
for
i
in
(
1
,
8
,
7
,
3
,
4
):
self
.
assertEqual
(
container
.
get
(
data
[
i
][
0
],
default
),
data
[
i
][
1
])
self
.
assertEqual
(
container
.
get
(
data
[
i
][
0
]),
data
[
i
][
1
])
def
test_values
(
self
):
# See interface IReadContainer
container
=
self
.
makeTestObject
()
values
=
container
.
values
()
self
.
assertEqual
(
list
(
values
),
[])
container
,
data
=
self
.
__setUp
()
values
=
list
(
container
.
values
())
for
k
,
v
in
data
:
try
:
values
.
remove
(
v
)
except
ValueError
:
self
.
fail
(
'Value not in list'
)
self
.
assertEqual
(
values
,
[])
def
test_len
(
self
):
# See interface IReadContainer
container
=
self
.
makeTestObject
()
self
.
assertEqual
(
len
(
container
),
0
)
container
,
data
=
self
.
__setUp
()
self
.
assertEqual
(
len
(
container
),
len
(
data
))
def
test_items
(
self
):
# See interface IReadContainer
container
=
self
.
makeTestObject
()
items
=
container
.
items
()
self
.
assertEqual
(
list
(
items
),
[])
container
,
data
=
self
.
__setUp
()
items
=
container
.
items
()
items
=
list
(
items
);
items
.
sort
()
# convert to sorted list
data
.
sort
()
# sort input data
self
.
assertEqual
(
items
,
data
)
def
test___contains__
(
self
):
# See interface IReadContainer
container
=
self
.
makeTestObject
()
data
=
self
.
makeTestData
()
self
.
assertEqual
(
not
not
(
data
[
6
][
0
]
in
container
),
False
)
container
,
data
=
self
.
__setUp
()
self
.
assertEqual
(
not
not
(
data
[
6
][
0
]
in
container
),
True
)
for
i
in
(
1
,
8
,
7
,
3
,
4
):
self
.
assertEqual
(
not
not
(
data
[
i
][
0
]
in
container
),
1
)
def
test_delObject
(
self
):
# See interface IWriteContainer
default
=
object
()
data
=
self
.
makeTestData
()
container
=
self
.
makeTestObject
()
self
.
assertRaises
(
KeyError
,
container
.
__delitem__
,
data
[
0
][
0
])
container
,
data
=
self
.
__setUp
()
self
.
assertRaises
(
KeyError
,
container
.
__delitem__
,
self
.
getUnknownKey
())
for
i
in
(
1
,
8
,
7
,
3
,
4
):
del
container
[
data
[
i
][
0
]]
for
i
in
(
1
,
8
,
7
,
3
,
4
):
self
.
assertRaises
(
KeyError
,
container
.
__getitem__
,
data
[
i
][
0
])
self
.
assertEqual
(
container
.
get
(
data
[
i
][
0
],
default
),
default
)
for
i
in
(
0
,
2
,
9
,
6
,
5
):
self
.
assertEqual
(
container
[
data
[
i
][
0
]],
data
[
i
][
1
])
############################################################
# Tests from Folder
def
testEmpty
(
self
):
folder
=
self
.
makeTestObject
()
data
=
self
.
makeTestData
()
self
.
failIf
(
folder
.
keys
())
self
.
failIf
(
folder
.
values
())
self
.
failIf
(
folder
.
items
())
self
.
failIf
(
len
(
folder
))
self
.
failIf
(
data
[
6
][
0
]
in
folder
)
self
.
assertEquals
(
folder
.
get
(
data
[
6
][
0
],
None
),
None
)
self
.
assertRaises
(
KeyError
,
folder
.
__getitem__
,
data
[
6
][
0
])
self
.
assertRaises
(
KeyError
,
folder
.
__delitem__
,
data
[
6
][
0
])
def
testBadKeyTypes
(
self
):
folder
=
self
.
makeTestObject
()
data
=
self
.
makeTestData
()
value
=
data
[
1
][
1
]
for
name
in
self
.
getBadKeyTypes
():
self
.
assertRaises
(
TypeError
,
folder
.
__setitem__
,
name
,
value
)
def
testOneItem
(
self
):
folder
=
self
.
makeTestObject
()
data
=
self
.
makeTestData
()
foo
=
data
[
0
][
1
]
name
=
data
[
0
][
0
]
folder
[
name
]
=
foo
self
.
assertEquals
(
len
(
folder
.
keys
()),
1
)
self
.
assertEquals
(
folder
.
keys
()[
0
],
name
)
self
.
assertEquals
(
len
(
folder
.
values
()),
1
)
self
.
assertEquals
(
folder
.
values
()[
0
],
foo
)
self
.
assertEquals
(
len
(
folder
.
items
()),
1
)
self
.
assertEquals
(
folder
.
items
()[
0
],
(
name
,
foo
))
self
.
assertEquals
(
len
(
folder
),
1
)
self
.
failUnless
(
name
in
folder
)
# Use an arbitrary id frpm the data set; don;t just use any id, since
# there might be restrictions on their form
self
.
failIf
(
data
[
6
][
0
]
in
folder
)
self
.
assertEquals
(
folder
.
get
(
name
,
None
),
foo
)
self
.
assertEquals
(
folder
[
name
],
foo
)
self
.
assertRaises
(
KeyError
,
folder
.
__getitem__
,
data
[
6
][
0
])
foo2
=
data
[
1
][
1
]
name2
=
data
[
1
][
0
]
folder
[
name2
]
=
foo2
self
.
assertEquals
(
len
(
folder
.
keys
()),
2
)
self
.
assertEquals
(
not
not
name2
in
folder
.
keys
(),
True
)
self
.
assertEquals
(
len
(
folder
.
values
()),
2
)
self
.
assertEquals
(
not
not
foo2
in
folder
.
values
(),
True
)
self
.
assertEquals
(
len
(
folder
.
items
()),
2
)
self
.
assertEquals
(
not
not
(
name2
,
foo2
)
in
folder
.
items
(),
True
)
self
.
assertEquals
(
len
(
folder
),
2
)
del
folder
[
name
]
del
folder
[
name2
]
self
.
failIf
(
folder
.
keys
())
self
.
failIf
(
folder
.
values
())
self
.
failIf
(
folder
.
items
())
self
.
failIf
(
len
(
folder
))
self
.
failIf
(
name
in
folder
)
self
.
assertRaises
(
KeyError
,
folder
.
__getitem__
,
name
)
self
.
assertEquals
(
folder
.
get
(
name
,
None
),
None
)
self
.
assertRaises
(
KeyError
,
folder
.
__delitem__
,
name
)
def
testManyItems
(
self
):
folder
=
self
.
makeTestObject
()
data
=
self
.
makeTestData
()
objects
=
[
data
[
i
][
1
]
for
i
in
range
(
4
)
]
name0
=
data
[
0
][
0
]
name1
=
data
[
1
][
0
]
name2
=
data
[
2
][
0
]
name3
=
data
[
3
][
0
]
folder
[
name0
]
=
objects
[
0
]
folder
[
name1
]
=
objects
[
1
]
folder
[
name2
]
=
objects
[
2
]
folder
[
name3
]
=
objects
[
3
]
self
.
assertEquals
(
len
(
folder
.
keys
()),
len
(
objects
))
self
.
failUnless
(
name0
in
folder
.
keys
())
self
.
failUnless
(
name1
in
folder
.
keys
())
self
.
failUnless
(
name2
in
folder
.
keys
())
self
.
failUnless
(
name3
in
folder
.
keys
())
self
.
assertEquals
(
len
(
folder
.
values
()),
len
(
objects
))
self
.
failUnless
(
objects
[
0
]
in
folder
.
values
())
self
.
failUnless
(
objects
[
1
]
in
folder
.
values
())
self
.
failUnless
(
objects
[
2
]
in
folder
.
values
())
self
.
failUnless
(
objects
[
3
]
in
folder
.
values
())
self
.
assertEquals
(
len
(
folder
.
items
()),
len
(
objects
))
self
.
failUnless
((
name0
,
objects
[
0
])
in
folder
.
items
())
self
.
failUnless
((
name1
,
objects
[
1
])
in
folder
.
items
())
self
.
failUnless
((
name2
,
objects
[
2
])
in
folder
.
items
())
self
.
failUnless
((
name3
,
objects
[
3
])
in
folder
.
items
())
self
.
assertEquals
(
len
(
folder
),
len
(
objects
))
self
.
failUnless
(
name0
in
folder
)
self
.
failUnless
(
name1
in
folder
)
self
.
failUnless
(
name2
in
folder
)
self
.
failUnless
(
name3
in
folder
)
self
.
failIf
(
data
[
5
][
0
]
in
folder
)
self
.
assertEquals
(
folder
.
get
(
name0
,
None
),
objects
[
0
])
self
.
assertEquals
(
folder
[
name0
],
objects
[
0
])
self
.
assertEquals
(
folder
.
get
(
name1
,
None
),
objects
[
1
])
self
.
assertEquals
(
folder
[
name1
],
objects
[
1
])
self
.
assertEquals
(
folder
.
get
(
name2
,
None
),
objects
[
2
])
self
.
assertEquals
(
folder
[
name2
],
objects
[
2
])
self
.
assertEquals
(
folder
.
get
(
name3
,
None
),
objects
[
3
])
self
.
assertEquals
(
folder
[
name3
],
objects
[
3
])
self
.
assertEquals
(
folder
.
get
(
data
[
5
][
0
],
None
),
None
)
self
.
assertRaises
(
KeyError
,
folder
.
__getitem__
,
data
[
5
][
0
])
del
folder
[
name0
]
self
.
assertEquals
(
len
(
folder
),
len
(
objects
)
-
1
)
self
.
failIf
(
name0
in
folder
)
self
.
failIf
(
name0
in
folder
.
keys
())
self
.
failIf
(
objects
[
0
]
in
folder
.
values
())
self
.
failIf
((
name0
,
objects
[
0
])
in
folder
.
items
())
self
.
assertEquals
(
folder
.
get
(
name0
,
None
),
None
)
self
.
assertRaises
(
KeyError
,
folder
.
__getitem__
,
name0
)
self
.
assertRaises
(
KeyError
,
folder
.
__delitem__
,
name0
)
del
folder
[
name1
]
del
folder
[
name2
]
del
folder
[
name3
]
self
.
failIf
(
folder
.
keys
())
self
.
failIf
(
folder
.
values
())
self
.
failIf
(
folder
.
items
())
self
.
failIf
(
len
(
folder
))
self
.
failIf
(
name0
in
folder
)
self
.
failIf
(
name1
in
folder
)
self
.
failIf
(
name2
in
folder
)
self
.
failIf
(
name3
in
folder
)
class
TestSampleContainer
(
BaseTestIContainer
,
TestCase
):
def
makeTestObject
(
self
):
from
zope.app.container.sample
import
SampleContainer
return
SampleContainer
()
def
makeTestData
(
self
):
return
DefaultTestData
()
def
getUnknownKey
(
self
):
return
'10'
def
getBadKeyTypes
(
self
):
return
[
None
,
[
'foo'
],
1
,
'
\
xf3
abc'
]
def
test_suite
():
return
makeSuite
(
TestSampleContainer
)
if
__name__
==
'__main__'
:
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/tests/test_objectcopier.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Object Copier Tests
$Id$
"""
from
unittest
import
TestCase
,
TestSuite
,
main
,
makeSuite
import
zope.component
from
zope.testing
import
doctest
from
zope.traversing.api
import
traverse
from
zope.component.eventtesting
import
getEvents
,
clearEvents
from
zope.copypastemove
import
ObjectCopier
from
zope.copypastemove.interfaces
import
IObjectCopier
from
zope.app.component.testing
import
PlacefulSetup
from
zope.app.testing
import
setup
from
zope.app.folder
import
Folder
class
File
(
object
):
pass
def
test_copy_events
():
"""
Prepare the setup::
>>> root = setup.placefulSetUp(site=True)
>>> zope.component.provideAdapter(ObjectCopier, (None,), IObjectCopier)
Prepare some objects::
>>> folder = Folder()
>>> root[u'foo'] = File()
>>> root[u'folder'] = folder
>>> list(folder.keys())
[]
>>> foo = traverse(root, 'foo') # wrap in ContainedProxy
Now make a copy::
>>> clearEvents()
>>> copier = IObjectCopier(foo)
>>> copier.copyTo(folder, u'bar')
u'bar'
Check that the copy has been done::
>>> list(folder.keys())
[u'bar']
Check what events have been sent::
>>> events = getEvents()
>>> [event.__class__.__name__ for event in events]
['ObjectCopiedEvent', 'ObjectAddedEvent', 'ContainerModifiedEvent']
Check that the ObjectCopiedEvent includes the correct data::
>>> events[0].object is folder[u'bar']
True
>>> events[0].original is root[u'foo']
True
Finally, tear down::
>>> setup.placefulTearDown()
"""
class
ObjectCopierTest
(
PlacefulSetup
,
TestCase
):
def
setUp
(
self
):
PlacefulSetup
.
setUp
(
self
)
PlacefulSetup
.
buildFolders
(
self
)
zope
.
component
.
provideAdapter
(
ObjectCopier
,
(
None
,),
IObjectCopier
)
def
test_copytosame
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
copier
=
IObjectCopier
(
file
)
copier
.
copyTo
(
container
,
'file1'
)
self
.
failUnless
(
'file1'
in
container
)
self
.
failUnless
(
'file1-2'
in
container
)
def
test_copytosamewithnewname
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
copier
=
IObjectCopier
(
file
)
copier
.
copyTo
(
container
,
'file2'
)
self
.
failUnless
(
'file1'
in
container
)
self
.
failUnless
(
'file2'
in
container
)
def
test_copytoother
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
target
=
traverse
(
root
,
'folder2'
)
file
=
traverse
(
root
,
'folder1/file1'
)
copier
=
IObjectCopier
(
file
)
copier
.
copyTo
(
target
,
'file1'
)
self
.
failUnless
(
'file1'
in
container
)
self
.
failUnless
(
'file1'
in
target
)
def
test_copytootherwithnewname
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
target
=
traverse
(
root
,
'folder2'
)
file
=
traverse
(
root
,
'folder1/file1'
)
copier
=
IObjectCopier
(
file
)
copier
.
copyTo
(
target
,
'file2'
)
self
.
failUnless
(
'file1'
in
container
)
self
.
failUnless
(
'file2'
in
target
)
def
test_copytootherwithnamecollision
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
target
=
traverse
(
root
,
'folder2'
)
target
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
copier
=
IObjectCopier
(
file
)
copier
.
copyTo
(
target
,
'file1'
)
# we do it twice, just to test auto-name generation
copier
.
copyTo
(
target
,
'file1'
)
self
.
failUnless
(
'file1'
in
container
)
self
.
failUnless
(
'file1'
in
target
)
self
.
failUnless
(
'file1-2'
in
target
)
self
.
failUnless
(
'file1-3'
in
target
)
def
test_copyable
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
copier
=
IObjectCopier
(
file
)
self
.
failUnless
(
copier
.
copyable
())
def
test_copyableTo
(
self
):
# A file should be copyable to a folder that has an
# object with the same id.
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
copier
=
IObjectCopier
(
file
)
self
.
failUnless
(
copier
.
copyableTo
(
container
,
'file1'
))
def
test_copyfoldertosibling
(
self
):
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder2'
)
source
=
traverse
(
root
,
'/folder1/folder1_1'
)
copier
=
IObjectCopier
(
source
)
copier
.
copyTo
(
target
)
self
.
failUnless
(
'folder1_1'
in
target
)
def
test_copyfoldertosame
(
self
):
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder1'
)
source
=
traverse
(
root
,
'/folder1/folder1_1'
)
copier
=
IObjectCopier
(
source
)
copier
.
copyTo
(
target
)
self
.
failUnless
(
'folder1_1'
in
target
)
def
test_copyfoldertosame2
(
self
):
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder1/folder1_1'
)
source
=
traverse
(
root
,
'/folder1/folder1_1/folder1_1_1'
)
copier
=
IObjectCopier
(
source
)
copier
.
copyTo
(
target
)
self
.
failUnless
(
'folder1_1_1'
in
target
)
def
test_copyfolderfromroot
(
self
):
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder2'
)
source
=
traverse
(
root
,
'/folder1'
)
copier
=
IObjectCopier
(
source
)
copier
.
copyTo
(
target
)
self
.
failUnless
(
'folder1'
in
target
)
def
test_copyfolderfromroot2
(
self
):
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder2/folder2_1/folder2_1_1'
)
source
=
traverse
(
root
,
'/folder1'
)
copier
=
IObjectCopier
(
source
)
copier
.
copyTo
(
target
)
self
.
failUnless
(
'folder1'
in
target
)
def
test_suite
():
return
TestSuite
((
makeSuite
(
ObjectCopierTest
),
doctest
.
DocTestSuite
(),
))
if
__name__
==
'__main__'
:
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/tests/test_objectmover.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Object Mover Tests
$Id$
"""
from
unittest
import
TestCase
,
TestSuite
,
main
,
makeSuite
import
zope.component
from
zope.testing
import
doctest
from
zope.traversing.api
import
traverse
from
zope.component.eventtesting
import
getEvents
,
clearEvents
from
zope.copypastemove
import
ObjectMover
from
zope.copypastemove.interfaces
import
IObjectMover
from
zope.app.component.testing
import
PlacefulSetup
from
zope.app.testing
import
setup
from
zope.app.folder
import
Folder
class
File
(
object
):
pass
def
test_move_events
():
"""
Prepare the setup::
>>> root = setup.placefulSetUp(site=True)
>>> zope.component.provideAdapter(ObjectMover, (None,), IObjectMover)
Prepare some objects::
>>> folder = Folder()
>>> root[u'foo'] = File()
>>> root[u'folder'] = folder
>>> list(folder.keys())
[]
>>> foo = traverse(root, 'foo') # wrap in ContainedProxy
Now move it::
>>> clearEvents()
>>> mover = IObjectMover(foo)
>>> mover.moveableTo(folder)
True
>>> mover.moveTo(folder, u'bar')
u'bar'
Check that the move has been done::
>>> list(root.keys())
[u'folder']
>>> list(folder.keys())
[u'bar']
Check what events have been sent::
>>> events = getEvents()
>>> [event.__class__.__name__ for event in events]
['ObjectMovedEvent', 'ContainerModifiedEvent', 'ContainerModifiedEvent']
Verify that the ObjectMovedEvent includes the correct data::
>>> events[0].oldName, events[0].newName
(u'foo', u'bar')
>>> events[0].oldParent is root
True
>>> events[0].newParent is folder
True
Let's look the other events:
>>> events[1].object is folder
True
>>> events[2].object is root
True
Finally, tear down::
>>> setup.placefulTearDown()
"""
class
ObjectMoverTest
(
PlacefulSetup
,
TestCase
):
def
setUp
(
self
):
PlacefulSetup
.
setUp
(
self
)
PlacefulSetup
.
buildFolders
(
self
)
zope
.
component
.
provideAdapter
(
ObjectMover
,
(
None
,),
)
def
test_movetosame
(
self
):
# Should be a noop, because "moving" to same location
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
mover
=
IObjectMover
(
file
)
mover
.
moveTo
(
container
,
'file1'
)
self
.
failUnless
(
'file1'
in
container
)
self
.
assertEquals
(
len
(
container
),
3
)
def
test_movetosamewithnewname
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
mover
=
IObjectMover
(
file
)
mover
.
moveTo
(
container
,
'file2'
)
self
.
failIf
(
'file1'
in
container
)
self
.
failUnless
(
'file2'
in
container
)
def
test_movetoother
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
target
=
traverse
(
root
,
'folder2'
)
file
=
traverse
(
root
,
'folder1/file1'
)
mover
=
IObjectMover
(
file
)
mover
.
moveTo
(
target
,
'file1'
)
self
.
failIf
(
'file1'
in
container
)
self
.
failUnless
(
'file1'
in
target
)
def
test_movetootherwithnewname
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
target
=
traverse
(
root
,
'folder2'
)
file
=
traverse
(
root
,
'folder1/file1'
)
mover
=
IObjectMover
(
file
)
mover
.
moveTo
(
target
,
'file2'
)
self
.
failIf
(
'file1'
in
container
)
self
.
failUnless
(
'file2'
in
target
)
def
test_movetootherwithnamecollision
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
target
=
traverse
(
root
,
'folder2'
)
target
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
mover
=
IObjectMover
(
file
)
mover
.
moveTo
(
target
,
'file1'
)
self
.
failIf
(
'file1'
in
container
)
self
.
failUnless
(
'file1'
in
target
)
self
.
failUnless
(
'file1-2'
in
target
)
def
test_moveable
(
self
):
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
mover
=
IObjectMover
(
file
)
self
.
failUnless
(
mover
.
moveable
())
def
test_moveableTo
(
self
):
# A file should be moveable to a folder that has an
# object with the same id.
root
=
self
.
rootFolder
container
=
traverse
(
root
,
'folder1'
)
container
[
'file1'
]
=
File
()
file
=
traverse
(
root
,
'folder1/file1'
)
mover
=
IObjectMover
(
file
)
self
.
failUnless
(
mover
.
moveableTo
(
container
,
'file1'
))
def
test_movefoldertosibling
(
self
):
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder2'
)
source
=
traverse
(
root
,
'/folder1/folder1_1'
)
mover
=
IObjectMover
(
source
)
mover
.
moveTo
(
target
)
self
.
failUnless
(
'folder1_1'
in
target
)
def
test_movefoldertosame
(
self
):
# Should be a noop, because "moving" to same location
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder1'
)
source
=
traverse
(
root
,
'/folder1/folder1_1'
)
mover
=
IObjectMover
(
source
)
mover
.
moveTo
(
target
)
self
.
failUnless
(
'folder1_1'
in
target
)
self
.
assertEquals
(
len
(
target
),
2
)
def
test_movefoldertosame2
(
self
):
# Should be a noop, because "moving" to same location
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder1/folder1_1'
)
source
=
traverse
(
root
,
'/folder1/folder1_1/folder1_1_1'
)
mover
=
IObjectMover
(
source
)
mover
.
moveTo
(
target
)
self
.
failUnless
(
'folder1_1_1'
in
target
)
self
.
assertEquals
(
len
(
target
),
2
)
def
test_movefolderfromroot
(
self
):
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder2'
)
source
=
traverse
(
root
,
'/folder1'
)
mover
=
IObjectMover
(
source
)
mover
.
moveTo
(
target
)
self
.
failUnless
(
'folder1'
in
target
)
def
test_movefolderfromroot2
(
self
):
root
=
self
.
rootFolder
target
=
traverse
(
root
,
'/folder2/folder2_1/folder2_1_1'
)
source
=
traverse
(
root
,
'/folder1'
)
mover
=
IObjectMover
(
source
)
mover
.
moveTo
(
target
)
self
.
failUnless
(
'folder1'
in
target
)
def
test_suite
():
return
TestSuite
((
makeSuite
(
ObjectMoverTest
),
doctest
.
DocTestSuite
(),
))
if
__name__
==
'__main__'
:
main
(
defaultTest
=
'test_suite'
)
src/zope/app/container/tests/test_ordered.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Test the OrderedContainer.
$Id$
"""
import
unittest
from
zope.testing.doctestunit
import
DocTestSuite
from
zope.component.eventtesting
import
getEvents
,
clearEvents
from
zope.app.testing
import
placelesssetup
,
setup
def
test_order_events
():
"""
Prepare the setup::
>>> root = setup.placefulSetUp(site=True)
Prepare some objects::
>>> from zope.app.container.ordered import OrderedContainer
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> oc['baz'] = 'quux'
>>> oc['zork'] = 'grue'
>>> oc.keys()
['foo', 'baz', 'zork']
Now change the order::
>>> clearEvents()
>>> oc.updateOrder(['baz', 'foo', 'zork'])
>>> oc.keys()
['baz', 'foo', 'zork']
Check what events have been sent::
>>> events = getEvents()
>>> [event.__class__.__name__ for event in events]
['ContainerModifiedEvent']
This is in fact a specialized modification event::
>>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent
>>> IObjectModifiedEvent.providedBy(events[0])
True
Finally, tear down::
>>> setup.placefulTearDown()
"""
def
test_all_items_available_at_object_added_event
():
"""
Prepare the setup::
>>> root = setup.placefulSetUp(site=True)
Now register an event subscriber to object added events.
>>> import zope.component
>>> from zope.app.container import interfaces
>>> @zope.component.adapter(interfaces.IObjectAddedEvent)
... def printContainerKeys(event):
... print event.newParent.keys()
>>> zope.component.provideHandler(printContainerKeys)
Now we are adding an object to the container.
>>> from zope.app.container.ordered import OrderedContainer
>>> oc = OrderedContainer()
>>> oc['foo'] = 'FOO'
['foo']
Finally, tear down::
>>> setup.placefulTearDown()
"""
def
test_suite
():
suite
=
unittest
.
TestSuite
()
suite
.
addTest
(
DocTestSuite
(
"zope.app.container.ordered"
,
setUp
=
placelesssetup
.
setUp
,
tearDown
=
placelesssetup
.
tearDown
))
suite
.
addTest
(
DocTestSuite
())
return
suite
if
__name__
==
'__main__'
:
unittest
.
main
()
src/zope/app/container/tests/test_size.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Test container ISized adapter.
$Id$
"""
import
unittest
from
zope.interface
import
implements
from
zope.size.interfaces
import
ISized
from
zope.app.container.interfaces
import
IContainer
class
DummyContainer
(
object
):
implements
(
IContainer
)
def
__init__
(
self
,
numitems
):
self
.
_numitems
=
numitems
def
__len__
(
self
):
return
self
.
_numitems
class
Test
(
unittest
.
TestCase
):
def
testImplementsISized
(
self
):
from
zope.app.container.size
import
ContainerSized
sized
=
ContainerSized
(
DummyContainer
(
23
))
self
.
assert_
(
ISized
.
providedBy
(
sized
))
def
testEmptyContainer
(
self
):
from
zope.app.container.size
import
ContainerSized
obj
=
DummyContainer
(
0
)
sized
=
ContainerSized
(
obj
)
self
.
assertEqual
(
sized
.
sizeForSorting
(),
(
'item'
,
0
))
self
.
assertEqual
(
sized
.
sizeForDisplay
(),
u'${items} items'
)
self
.
assertEqual
(
sized
.
sizeForDisplay
().
mapping
[
'items'
],
'0'
)
def
testOneItem
(
self
):
from
zope.app.container.size
import
ContainerSized
obj
=
DummyContainer
(
1
)
sized
=
ContainerSized
(
obj
)
self
.
assertEqual
(
sized
.
sizeForSorting
(),
(
'item'
,
1
))
self
.
assertEqual
(
sized
.
sizeForDisplay
(),
u'1 item'
)
def
testSeveralItems
(
self
):
from
zope.app.container.size
import
ContainerSized
obj
=
DummyContainer
(
2
)
sized
=
ContainerSized
(
obj
)
self
.
assertEqual
(
sized
.
sizeForSorting
(),
(
'item'
,
2
))
self
.
assertEqual
(
sized
.
sizeForDisplay
(),
u'${items} items'
)
self
.
assertEqual
(
sized
.
sizeForDisplay
().
mapping
[
'items'
],
'2'
)
def
test_suite
():
loader
=
unittest
.
TestLoader
()
return
loader
.
loadTestsFromTestCase
(
Test
)
if
__name__
==
'__main__'
:
unittest
.
TextTestRunner
().
run
(
test_suite
())
src/zope/app/container/traversal.py
0 → 100644
View file @
160df616
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Traversal components for containers
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.interface
import
implements
from
zope.component
import
queryMultiAdapter
from
zope.traversing.interfaces
import
TraversalError
,
ITraversable
from
zope.publisher.interfaces.browser
import
IBrowserPublisher
from
zope.publisher.interfaces.xmlrpc
import
IXMLRPCPublisher
from
zope.publisher.interfaces
import
NotFound
from
zope.app
import
zapi
from
zope.app.container.interfaces
import
ISimpleReadContainer
,
IItemContainer
from
zope.app.container.interfaces
import
IReadContainer
# Note that the next two classes are included here because they
# can be used for multiple view types.
class
ContainerTraverser
(
object
):
"""A traverser that knows how to look up objects by name in a container."""
implements
(
IBrowserPublisher
,
IXMLRPCPublisher
)
__used_for__
=
ISimpleReadContainer
def
__init__
(
self
,
container
,
request
):
self
.
context
=
container
self
.
request
=
request
def
publishTraverse
(
self
,
request
,
name
):
"""See zope.publisher.interfaces.IPublishTraverse"""
subob
=
self
.
context
.
get
(
name
,
None
)
if
subob
is
None
:
view
=
queryMultiAdapter
((
self
.
context
,
request
),
name
=
name
)
if
view
is
not
None
:
return
view
raise
NotFound
(
self
.
context
,
name
,
request
)
return
subob
def
browserDefault
(
self
,
request
):
"""See zope.publisher.browser.interfaces.IBrowserPublisher"""
view_name
=
zapi
.
getDefaultViewName
(
self
.
context
,
request
)
view_uri
=
"@@%s"
%
view_name
return
self
.
context
,
(
view_uri
,)
class
ItemTraverser
(
ContainerTraverser
):
"""A traverser that knows how to look up objects by name in an item
container."""
__used_for__
=
IItemContainer
def
publishTraverse
(
self
,
request
,
name
):
"""See zope.publisher.interfaces.IPublishTraverse"""
try
:
return
self
.
context
[
name
]
except
KeyError
:
view
=
queryMultiAdapter
((
self
.
context
,
request
),
name
=
name
)
if
view
is
not
None
:
return
view
raise
NotFound
(
self
.
context
,
name
,
request
)
_marker
=
object
()
class
ContainerTraversable
(
object
):
"""Traverses containers via `getattr` and `get`."""
implements
(
ITraversable
)
__used_for__
=
IReadContainer
def
__init__
(
self
,
container
):
self
.
_container
=
container
def
traverse
(
self
,
name
,
furtherPath
):
container
=
self
.
_container
v
=
container
.
get
(
name
,
_marker
)
if
v
is
_marker
:
v
=
getattr
(
container
,
name
,
_marker
)
if
v
is
_marker
:
raise
TraversalError
(
container
,
name
)
return
v
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