[Checkins] SVN: Sandbox/ulif/grok-adminui/src/grok/ New Object
browser for the admin-UI. Several minor changes. Sorry for the mess.
Uli Fouquet
uli at gnufix.de
Mon Aug 6 11:00:39 EDT 2007
Log message for revision 78628:
New Object browser for the admin-UI. Several minor changes. Sorry for the mess.
Changed:
U Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.py
A Sandbox/ulif/grok-adminui/src/grok/admin/inspect.txt
A Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.py
A Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.txt
U Sandbox/ulif/grok-adminui/src/grok/admin/static/grok.css
U Sandbox/ulif/grok-adminui/src/grok/admin/tests/test_grokadmin.py
A Sandbox/ulif/grok-adminui/src/grok/admin/utilities.py
U Sandbox/ulif/grok-adminui/src/grok/admin/view.py
U Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/applications.pt
U Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokpackageview.pt
U Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokview.pt
U Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/inspect.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/logout.pt
U Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt
U Sandbox/ulif/grok-adminui/src/grok/ftests/admin/apps.py
A Sandbox/ulif/grok-adminui/src/grok/ftests/admin/loginlogout.py
A Sandbox/ulif/grok-adminui/src/grok/ftests/admin/objectbrowser.py
-=-
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.py 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.py 2007-08-06 15:00:38 UTC (rev 78628)
@@ -7,6 +7,13 @@
"""
+import os
+import sys # for sys.path
+import types
+import grok
+import inspect
+from urlparse import urlparse, urlunparse
+
import zope.component
from zope.app.folder.interfaces import IRootFolder
from zope.dottedname.resolve import resolve
@@ -15,18 +22,7 @@
from zope.security.proxy import removeSecurityProxy
from zope.proxy import removeAllProxies
-import os
-import sys # for sys.path
-import types
-import grok
-import inspect
-import grok.interfaces
-from grok.interfaces import IApplication
-from martian.scan import is_package, ModuleInfo
-from martian import InstanceGrokker, ModuleGrokker
-
from zope.app.i18n import ZopeMessageFactory as _
-
from zope.app.apidoc.codemodule.module import Module
from zope.app.apidoc.codemodule.class_ import Class
from zope.app.apidoc.codemodule.text import TextFile
@@ -35,11 +31,20 @@
from zope.app.apidoc.utilities import getPythonPath, getPermissionIds
from zope.app.apidoc.utilities import isReferencable
+import grok.interfaces
+from grok.interfaces import IApplication
+from martian.scan import is_package, ModuleInfo
+from martian import InstanceGrokker, ModuleGrokker
+from grok.admin.objectinfo import ZopeObjectInfo
+# This is the name under which the docgrok object-browser can be
+# reached.
+DOCGROK_ITEM_NAMESPACE = 'docgrok-obj'
+
grok.context(IRootFolder)
grok.define_permission('grok.ManageApplications')
-def find_filepath( dotted_path ):
+def find_filepath(dotted_path):
"""Find the filepath for a dotted name.
If a dotted name denotes a filename we try to find its path
@@ -66,8 +71,8 @@
currname = "." + currname
currname = name + currname
tmp_path = ""
- for elem in currpath.split( '.' ):
- tmp_path = os.path.join( tmp_path, elem )
+ for elem in currpath.split('.'):
+ tmp_path = os.path.join(tmp_path, elem)
for syspath in sys.path:
filepath_to_check = os.path.join(syspath, tmp_path, currname)
if os.path.isfile(filepath_to_check):
@@ -75,12 +80,12 @@
return None
-def handle_module( dotted_path, ob=None ):
+def handle_module(dotted_path, ob=None):
"""Determine, whether the given path/obj references a Python module.
"""
if ob is None:
try:
- ob = resolve( dotted_path )
+ ob = resolve(dotted_path)
except ImportError:
return None
if not hasattr(ob, '__file__'):
@@ -93,12 +98,12 @@
return None
return DocGrokModule(dotted_path)
-def handle_package( dotted_path, ob=None):
+def handle_package(dotted_path, ob=None):
"""Determine, whether the given path/obj references a Python package.
"""
if ob is None:
try:
- ob = resolve( dotted_path )
+ ob = resolve(dotted_path)
except ImportError:
return None
if not hasattr(ob, '__file__'):
@@ -136,7 +141,7 @@
return None
return DocGrokClass(dotted_path)
-def handle_grokapplication( dotted_path, ob=None):
+def handle_grokapplication(dotted_path, ob=None):
"""Determine, whether the given path/obj references a Grok application.
"""
if ob is None:
@@ -145,17 +150,17 @@
except ImportError:
None
try:
- if not IApplication.implementedBy( ob ):
+ if not IApplication.implementedBy(ob):
return None
except TypeError:
return None
return DocGrokGrokApplication(dotted_path)
-def handle_textfile( dotted_path, ob=None):
+def handle_textfile(dotted_path, ob=None):
if ob is not None:
# Textfiles that are objects, are not text files.
return None
- if os.path.splitext( dotted_path )[1] != u'.txt':
+ if os.path.splitext(dotted_path)[1] != u'.txt':
return None
return DocGrokTextFile(dotted_path)
@@ -182,11 +187,11 @@
"""Find a doctor specialized for certain things.
"""
try:
- ob = resolve( dotted_path )
+ ob = resolve(dotted_path)
except ImportError:
# There is no object of that name. Give back 404.
- # XXX Do something more intelligent, offer a search.
- if not find_filepath( dotted_path ):
+ # TODO: Do something more intelligent, offer a search.
+ if not find_filepath(dotted_path):
return None
ob = None
except:
@@ -194,7 +199,7 @@
for handler in docgrok_handlers:
spec_handler = handler['handler']
- doc_grok = spec_handler( dotted_path, ob )
+ doc_grok = spec_handler(dotted_path, ob)
if doc_grok is None:
continue
return doc_grok
@@ -279,7 +284,7 @@
Now we want to register this new DocGrok with the 'global
machinery'. Easy::
- >>> module_grokker.grok( 'mammoth_grokker', mammoth )
+ >>> module_grokker.grok('mammoth_grokker', mammoth)
True
Now the 'handle_mammoths' function is considered to deliver a
@@ -292,7 +297,7 @@
mammoths (if you defined one; otherwise the default 'DocGrok'
template will be used to show mammoth information).
- XXX TODO: Show how to make a docgrok view.
+ TODO: Show how to make a docgrok view.
That's it.
@@ -304,8 +309,7 @@
return False
if name in [x['name'] for x in docgrok_handlers]:
return False
- #docgrok_handlers[name] = obj
- docgrok_handlers.insert( 0, {'name':name,
+ docgrok_handlers.insert(0, {'name':name,
'handler':obj})
return True
@@ -321,24 +325,18 @@
DocGrok offers a minimum of information but can easily be extended in
derived classes.
"""
- msg = "I am Dr. Grok. How can I help you?"
path = None
_traversal_root = None
- #_children = {}
- def __init__(self, dotted_path ):
- #super( DocGrok, self ).__init__()
+ def __init__(self, dotted_path):
self.path = dotted_path
def getPath(self):
return self.path
- def getMsg(self):
- return self.msg
-
- def getFilePath( self ):
+ def getFilePath(self):
try:
- ob = resolve( self.path )
+ ob = resolve(self.path)
return hasattr(ob, __file__) and os.path.dirname(ob.__file__) or None
except ImportError:
pass
@@ -347,8 +345,8 @@
def getDoc(self, heading_only=False):
"""Get the doc string of the module STX formatted.
"""
- if hasattr( self, "apidoc") and hasattr(
- self.apidoc, "getDocString" ):
+ if hasattr(self, "apidoc") and hasattr(
+ self.apidoc, "getDocString"):
text = self.apidoc.getDocString()
else:
return None
@@ -384,14 +382,12 @@
else:
newpath = '.'.join([self.path, patient])
- #doctor = getDocGrokForDottedPath( newpath )
- doctor = handle( newpath )
+ doctor = handle(newpath)
if doctor is None:
# There is nothing of that name. Give back 404.
# XXX Do something more intelligent, offer a search.
return None
- #doctor.msg = "Do more grokking!"
doctor.__parent__ = self
doctor.__name__ = patient
doctor._traversal_root = self._traversal_root
@@ -399,6 +395,30 @@
return doctor
pass
+def getItemLink(name, baseurl):
+ """Get a link to a docgrok item out of an item name and a base URL.
+
+ A docgrok item is any object, which is a 'subobject' of another
+ object, for example an attribute, a memberfunction, an annotation
+ or a sequence item (if the parent object is a sequence). Those
+ objects are not neccessarily directly accessible, but need to be
+ denoted for the object browser. We put 'docgrok-item' as marker at
+ the beginning of the URL to enable the docgrok traverser and to
+ handle those URLs in a special way. Such the objectbrowser can
+ handle those 'unaccessible' items.
+ """
+ url = list(urlparse(baseurl))
+ path = url[2]
+ if path.startswith('/' + DOCGROK_ITEM_NAMESPACE + '/'):
+ path = path[len(DOCGROK_ITEM_NAMESPACE) + 2:]
+ if path.endswith('/@@inspect.html') or path.endswith('/inspect.html'):
+ path = path.rsplit('/', 1)
+ path = "/%s/%s%s/@@inspect.html" % (DOCGROK_ITEM_NAMESPACE, path, name)
+ path = path.replace('//', '/')
+ url[2] = path
+ return urlunparse(url)
+
+
class DocGrokTraverser(grok.Traverser):
"""If first URL element is 'docgrok', handle over to DocGrok.
@@ -410,6 +430,13 @@
grok.context(IRootFolder)
def traverse(self,path):
+ if path == DOCGROK_ITEM_NAMESPACE:
+ # The objectbrowser is called...
+ obj_info = ZopeObjectInfo(self.context)
+ obj_info.__parent__ = self.context
+ obj_info.__name__ = DOCGROK_ITEM_NAMESPACE
+ return obj_info
+
if path == "docgrok":
doctor = DocGrok(None)
# Giving a __parent__ and a __name__, we make things
@@ -424,7 +451,6 @@
class DocGrokPackage(DocGrok):
"""This doctor cares for python packages.
"""
- msg = "I am a Package of the Doc"
path=None
apidoc = None
_traversal_root = None
@@ -433,65 +459,64 @@
self.path = dotted_path
self._module = resolve(self.path)
# In apidoc packages are handled like modules...
- self.apidoc = Module( None, None, self._module, True)
+ self.apidoc = Module(None, None, self._module, True)
- def getDocString( self ):
+ def getDocString(self):
return self.apidoc.getDocString()
- def getFilePath( self ):
- ob = resolve( self.path )
- return os.path.dirname( ob.__file__ ) + '/'
+ def getFilePath(self):
+ ob = resolve(self.path)
+ return os.path.dirname(ob.__file__) + '/'
- def _getModuleInfos( self, filter_func=lambda x:x ):
+ def _getModuleInfos(self, filter_func=lambda x:x):
"""Get modules and packages of a package.
The filter function will be applied to a list of modules and
packages of type grok.scan.ModuleInfo.
"""
- ob = resolve( self.path )
+ ob = resolve(self.path)
filename = ob.__file__
- module_info = ModuleInfo( filename, self.path )
+ module_info = ModuleInfo(filename, self.path)
infos = module_info.getSubModuleInfos()
if filter_func is not None:
- infos = filter( filter_func, infos)
- #infos = [x for x in infos if not x.isPackage()]
+ infos = filter(filter_func, infos)
result = []
for info in infos:
subresult = {}
# Build a url string from dotted path...
mod_path = "docgrok"
for path_part in info.dotted_name.split('.'):
- mod_path = os.path.join( mod_path, path_part )
+ mod_path = os.path.join(mod_path, path_part)
subresult = {
'url' : mod_path,
'name' : info.name,
'dotted_name' : info.dotted_name
}
- result.append( subresult )
+ result.append(subresult)
return result
- def getModuleInfos( self ):
+ def getModuleInfos(self):
"""Get the modules inside a package.
"""
filter_func = lambda x: not x.isPackage()
- return self._getModuleInfos( filter_func )
+ return self._getModuleInfos(filter_func)
- def getSubPackageInfos( self ):
+ def getSubPackageInfos(self):
"""Get the subpackages inside a package.
"""
filter_func = lambda x: x.isPackage()
- return self._getModuleInfos( filter_func )
+ return self._getModuleInfos(filter_func)
- def getTextFiles( self ):
+ def getTextFiles(self):
"""Get the text files inside a package.
"""
- filter_func = lambda x: x.isinstance( TextFile )
- return self._getModuleInfos( filter_func )
+ filter_func = lambda x: x.isinstance(TextFile)
+ return self._getModuleInfos(filter_func)
- def getChildren( self ):
+ def getChildren(self):
result = self.apidoc.items()
- result.sort( lambda x,y:cmp(x[0], y[0]) )
+ result.sort(lambda x,y:cmp(x[0], y[0]))
return result
@@ -500,8 +525,8 @@
"""This doctor cares for python modules.
"""
- def getFilePath( self ):
- ob = resolve( self.path )
+ def getFilePath(self):
+ ob = resolve(self.path)
filename = ob.__file__
if filename.endswith('o') or filename.endswith('c'):
filename = filename[:-1]
@@ -515,12 +540,12 @@
self.path = dotted_path
self.klass = resolve(self.path)
self.module_path, self.name = dotted_path.rsplit('.',1)
- self.module = resolve( self.module_path )
- mod_apidoc = Module( None, None, self.module, False)
- self.apidoc = Class( mod_apidoc, self.name, self.klass)
+ self.module = resolve(self.module_path)
+ mod_apidoc = Module(None, None, self.module, False)
+ self.apidoc = Class(mod_apidoc, self.name, self.klass)
def getFilePath(self):
- if not hasattr( self.module, "__file__" ):
+ if not hasattr(self.module, "__file__"):
return None
filename = self.module.__file__
if filename.endswith('o') or filename.endswith('c'):
@@ -578,12 +603,12 @@
self.path = dotted_path
self.klass = resolve(self.path)
self.module_path, self.name = dotted_path.rsplit('.',1)
- self.module = resolve( self.module_path )
- mod_apidoc = Module( None, None, self.module, False)
- self.apidoc = Class( mod_apidoc, self.name, self.klass)
+ self.module = resolve(self.module_path)
+ mod_apidoc = Module(None, None, self.module, False)
+ self.apidoc = Class(mod_apidoc, self.name, self.klass)
- def getFilePath( self ):
- if not hasattr( self.module, "__file__" ):
+ def getFilePath(self):
+ if not hasattr(self.module, "__file__"):
return None
filename = self.module.__file__
if filename.endswith('o') or filename.endswith('c'):
@@ -601,14 +626,13 @@
def __init__(self,dotted_path):
self.path = dotted_path
- self.filepath = find_filepath( self.path )
- self.filename = os.path.basename( self.filepath )
+ self.filepath = find_filepath(self.path)
+ self.filename = os.path.basename(self.filepath)
def getPackagePath(self):
"""Return package path as dotted name.
"""
- #return os.path.dirname( self.filepath )
dot_num_in_filename = len([x for x in self.filename if x == '.'])
parts = self.path.rsplit('.', dot_num_in_filename + 1)
return parts[0]
Added: Sandbox/ulif/grok-adminui/src/grok/admin/inspect.txt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/inspect.txt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/inspect.txt 2007-08-06 15:00:38 UTC (rev 78628)
@@ -0,0 +1,101 @@
+=======
+Inspect
+=======
+
+Grok's own object inspector.
+
+The Grok object browser is basically simply a view on objects.
+
+.. contents::
+
+
+How to use it
+-------------
+
+You can use the object inspector through-the-web (TTW) or using
+Python. It was primarily designed for TTW-use. This is explained
+here.
+
+
+Simple usage: Inspecting easy reachable objects
+-----------------------------------------------
+
+The inspector is called by appending ``/@@inspect.html`` to an URL
+describing an object in the ZODB. For example, to watch the object
+description of your Zopes root folder, you can use something like:
+
+ http://localhost:8080/@@inspect.html
+
+Note, that not all URLs you type into your browser denote
+objects. They often describe only certain views of certain
+objects. Currently the browsing of views with the object inspector is
+not supported.
+
+If you want to examine another object, which is reachable 'via URL',
+just get the path you would normally enter into the browser and append
+``@@inspect.html``.
+
+Assuming you once created an object, which is reachable by::
+
+ http://localhost:8080/my_object
+
+then you can examine it via the object browser, using the following
+URL::
+
+ http://localhost:8080/my_object/@@inspect.html
+
+Easy, isn't it?
+
+In most cases you can also skip the 'eyes', the strange ``@@`` symbol
+in front of the view name (``inspect.html``). The 'eyes' are only
+neccessary, when you examine an object, which has got an own attribute
+called ``inspect.html``.
+
+
+Extended usage: Inspecting 'hidden' objects
+-------------------------------------------
+
+All objects we exmined so far, are 'easy reachable'. This means, they
+are reachable by entering a simple URL. However, some (to tell the
+truth: many) objects cannot be reached this way. Such objects might be
+'subobjects' of others, they might be handled by a special traverser,
+simply have no view or whatever. We call those objects here 'hidden
+objects'. What, if we want to know more about
+those hidden objects?
+
+For example, we might have the attribute of an object (which is an
+object as well), which is not directly callable::
+
+ http://localhost:8080/my_object/hidden_attribute
+
+might so generate an error. It does not help to append simply
+``@@inspect.html`` to the URL. Instead we have to call a special
+traverser, which is able to look up the object for us and helps
+out.
+
+This traverser is called by inserting ``docgrok-obj/`` as root of
+the path in the URL. The above mentioned hidden_attribute can
+therefore be reached using::
+
+ http://localhost:8080/docgrok_obj/my_object/hidden_attribute/@@inspect.html
+
+Such, you can reach even deeply nested subobjects of subobjects.
+
+Just think of ``docgrok-obj`` as a special URL-namespace like
+``++annotations++``, ``++view++`` or similar (in fact it is not a
+URL-namespace), which can examine the complete object tree stored in
+the ZODB. In fact this 'namespace' can be used with every object, also
+with 'visible' ones. The above call to see ``my_object`` could
+therefore be reached (in a more complicated way) as::
+
+ http://localhost:8080/docgrok_obj/my_object/@@inspect.html
+
+This is the most general form to call the object browser:
+
+ http://<hostname>[:<port>]/docgrok_obj/<object-path>/@@inspect.html
+
+where 'docgrok-obj/' and '@@inspect.html' are the object browser
+specific parts.
+
+
+
Added: Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.py (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.py 2007-08-06 15:00:38 UTC (rev 78628)
@@ -0,0 +1,805 @@
+"""Information about objects.
+
+This module provides wrappers/proies to give information about
+objects at runtime. See inspect.txt to learn more about retrieving
+object information using ObjectInfos.
+"""
+import inspect
+import re
+import types
+from types import MethodType
+
+import grok
+from grok.admin.utilities import isContainingEvilRegExpChars
+
+import zope
+from zope.interface import Interface, implementedBy
+from zope.interface.common.sequence import IExtendedReadSequence
+from zope.interface.common.mapping import IEnumerableMapping
+from zope.annotation.interfaces import IAnnotatable, IAnnotations
+from zope.app.apidoc import utilities
+from zope.location import location
+from zope.schema import getFields
+from zope.security.checker import getCheckerForInstancesOf
+from zope.security.proxy import removeSecurityProxy
+from zope.traversing.api import getParent, getRoot
+from zope.traversing.interfaces import IPhysicallyLocatable
+from zope.dublincore.interfaces import IZopeDublinCore
+from zope.app.folder import rootFolder
+
+
+
+class ObjectInfo(grok.Model):
+ """An inspect proxy to provide object specific data.
+
+ This is a base proxy, which can handle all (and only)
+ Python-related data. See objectinfo.txt to learn more about
+ ObjectInfos.
+ """
+ obj = None
+
+ def __init__(self, obj):
+ self.obj = obj
+
+ def getmembers(self, predicate=None):
+ """Return all the members of an object in a list of (name,
+ value) pairs sorted by name.
+
+ If the optional predicate argument is supplied, only members
+ for which the predicate returns a true value are included.
+ """
+ return inspect.getmembers(self.obj, predicate)
+
+ def getmoduleinfo(self):
+ """Get information about modules.
+
+ Return a tuple of values that describe how Python will
+ interpret the file identified by path if it is a module, or
+ None if it would not be identified as a module. The return
+ tuple is (name, suffix, mode, mtype), where name is the name
+ of the module without the name of any enclosing package,
+ suffix is the trailing part of the file name (which may not be
+ a dot-delimited extension), mode is the open() mode that would
+ be used ('r' or 'rb'), and mtype is an integer giving the type
+ of the module. mtype will have a value which can be compared
+ to the constants defined in the imp module; see the
+ documentation for that module for more information on module
+ types.
+ """
+ path = getattr(self.obj, '__file__', None)
+ if path is None:
+ return None
+ return inspect.getmoduleinfo(path)
+
+ def getmodulename(self):
+ """Return the name of the module named by the file path,
+ without including the names of enclosing packages. This uses
+ the same algorithm as the interpreter uses when searching for
+ modules. If the name cannot be matched according to the
+ interpreter's rules, None is returned.
+ """
+ path = getattr(self.obj, '__file__', None)
+ if path is None:
+ return None
+ return inspect.getmodulename(path)
+
+ def ismodule(self):
+ """Return true if the object is a module.
+ """
+ return inspect.ismodule(self.obj)
+
+ def isclass(self):
+ """Return true if the object is a class.
+ """
+ return inspect.isclass(self.obj)
+
+ def ismethod(self):
+ """Return true if the object is a method.
+ """
+ return inspect.ismethod(self.obj)
+
+ def isfunction(self):
+ """Return true if the object is a Python function or unnamed
+ (lambda) function.
+ """
+ return inspect.isfunction(self.obj)
+
+ def istraceback(self):
+ """Return true if the object is a traceback.
+ """
+ return inspect.istraceback(self.obj)
+
+ def isframe(self):
+ """Return true if the object is a frame.
+ """
+ return inspect.isframe(self.obj)
+
+ def iscode(self):
+ """Return true if the object is a code.
+ """
+ return inspect.iscode(self.obj)
+
+ def isbuiltin(self):
+ """Return true if the object is a built-in function.
+ """
+ return inspect.isbuiltin(self.obj)
+
+ def isroutine(self):
+ """Return true if the object is a user-defined or built-in
+ function or method.
+ """
+ return inspect.isroutine(self.obj)
+
+ def ismethoddescriptor(self):
+ """Return true if the object is a method descriptor, but not
+ if ismethod() or isclass() or isfunction() are true.
+
+ This is new as of Python 2.2, and, for example, is true of
+ int.__add__. An object passing this test has a __get__
+ attribute but not a __set__ attribute, but beyond that the set
+ of attributes varies. __name__ is usually sensible, and
+ __doc__ often is.
+
+ Methods implemented via descriptors that also pass one of the
+ other tests return false from the ismethoddescriptor() test,
+ simply because the other tests promise more - you can, e.g.,
+ count on having the im_func attribute (etc) when an object
+ passes ismethod().
+ """
+ return inspect.ismethoddescriptor(self.obj)
+
+ def isdatadescriptor(self):
+ """Return true if the object is a data descriptor.
+
+ Data descriptors have both a __get__ and a __set__
+ attribute. Examples are properties (defined in Python),
+ getsets, and members. The latter two are defined in C and
+ there are more specific tests available for those types, which
+ is robust across Python implementations. Typically, data
+ descriptors will also have __name__ and __doc__ attributes
+ (properties, getsets, and members have both of these
+ attributes), but this is not guaranteed. New in version 2.3.
+ """
+ return inspect.isdatadescriptor(self.obj)
+
+ def isgetsetdescriptor(self):
+ """Return true if the object is a getset descriptor.
+
+ getsets are attributes defined in extension modules via
+ PyGetSetDef structures. For Python implementations without
+ such types, this method will always return False.
+
+ New in version 2.5.
+ """
+ return inspect.isgetsetdescriptor(self.obj)
+
+ def ismemberdescriptor(self):
+ """Return true if the object is a member descriptor.
+
+ Member descriptors are attributes defined in extension modules
+ via PyMemberDef structures. For Python implementations without
+ such types, this method will always return False.
+
+ New in version 2.5.
+ """
+ return inspect.ismemberdescriptor(self.obj)
+
+ #
+ # Source code related...
+ #
+ def getdoc(self):
+ """Get the documentation string for an object.
+
+ All tabs are expanded to spaces. To clean up docstrings that
+ are indented to line up with blocks of code, any whitespace
+ than can be uniformly removed from the second line onwards is
+ removed.
+ """
+ return inspect.getdoc(self.obj)
+
+ def getcomments(self):
+ """Get comments for an object.
+
+ Return in a single string any lines of comments immediately
+ preceding the object's source code (for a class, function, or
+ method), or at the top of the Python source file (if the
+ object is a module).
+
+ Due to a bug in ``inspect.getsource()`` no objects can be
+ handled, which contain a regular expression specific char in
+ their string representation.
+
+ """
+ if isContainingEvilRegExpChars(str(self.obj)):
+ return None
+ return inspect.getcomments(self.obj)
+
+ def getfile(self):
+ """Return the name of the (text or binary) file in which an
+ object was defined.
+
+ If the object is a built-in module, class or function,
+ ``None`` will be returned.
+ """
+ try:
+ return inspect.getfile(self.obj)
+ except TypeError:
+ return
+
+ def getmodule(self):
+ """Try to guess which module an object was defined in.
+ """
+ return inspect.getmodule(self.obj)
+
+ def getsourcefile(self):
+ """Return the name of the Python source file in which an
+ object was defined.
+
+ If the object is a built-in module, class or function,
+ ``None`` will be returned.
+ """
+ try:
+ return inspect.getsourcefile(self.obj)
+ except TypeError:
+ return
+
+ def getsourcelines(self):
+ """Return a list of source lines and starting line number for
+ an object.
+
+ The argument may be a module, class, method, function,
+ traceback, frame, or code object. The source code is returned
+ as a list of the lines corresponding to the object and the
+ line number indicates where in the original source file the
+ first line of code was found. An IOError is raised if the
+ source code cannot be retrieved.
+ """
+ try:
+ return inspect.getsourcelines(self.obj)
+ except TypeError:
+ return
+ return
+
+ def getsource(self):
+ """Return the text of the source code for an object.
+
+ The argument may be a module, class, method, function,
+ traceback, frame, or code object. The source code is returned
+ as a single string. An IOError is raised if the source code
+ cannot be retrieved.
+
+ Due to a bug in ``inspect.getsource()`` no objects can be
+ handled, which contain a regular expression specific char in
+ their string representation.
+ """
+ if isContainingEvilRegExpChars(str(self.obj)):
+ return None
+
+ try:
+ return inspect.getsource(self.obj)
+ except TypeError:
+ return
+ return
+
+ def traverse(self, name):
+ new_obj = None
+
+ # Try to get name as dict entry...
+ keygetter = getattr(self.obj, 'keys', None)
+ if inspect.ismethod(keygetter):
+ if name in keygetter():
+ new_obj = self.obj[name]
+
+ # Try to get name as sequence entry...
+ if not new_obj:
+ # This is not the appropriate way to handle iterators. We
+ # must find somehing to handle them too.
+ try:
+ name_int = int(name)
+ if name_int in range(0, len(self.obj)):
+ new_obj = self.obj[name_int]
+ except ValueError:
+ pass
+
+ # Get name as obj attribute...
+ if not new_obj and hasattr(self.obj, name):
+ new_obj = getattr(self.obj, name, None)
+
+ # Get name as annotation...
+ if not new_obj:
+ naked = zope.security.proxy.removeSecurityProxy(self.obj)
+ try:
+ annotations = IAnnotations(naked)
+ new_obj = name and name in annotations and annotations[name]
+ if not new_obj:
+ new_obj = annotations
+ except TypeError:
+ pass
+
+ # Give obj a location...
+ if new_obj:
+ if not IPhysicallyLocatable(new_obj, False):
+ new_obj = location.LocationProxy(
+ new_obj, self.obj, name)
+
+ new_info = ZopeObjectInfo(new_obj)
+ new_info.__parent__ = self
+ new_info.__name__ = name
+ return new_info
+
+ # name not found...
+ return
+
+
+
+
+
+class ZopeObjectInfo(ObjectInfo):
+ """Zope specific data.
+ """
+
+ def __init__(self, obj):
+ self.obj = obj
+ self.__klass = getattr(obj, '__class__', None) or type(obj)
+ self.__interfaces = tuple(implementedBy(self.__klass))
+
+ def getTypeLink(self, obj_type):
+ if obj_type is types.NoneType:
+ return None
+ path = utilities.getPythonPath(obj_type)
+ return path.replace('.', '/')
+
+ def isLinkable(self, obj):
+ """We consider all but some basic types to be linkable for docgrok.
+
+ Although even simple strings can be displayed by a docgrok, it
+ does not make much sense. We therefore simply forbid such
+ links, filtering objects of basic types out.
+ """
+ for typespec in [types.NoneType, types.TypeType, types.BooleanType,
+ types.IntType, types.LongType, types.FloatType,
+ types.ComplexType, types.StringTypes,
+ types.MethodType, types.BuiltinFunctionType,
+ types.LambdaType, types.GeneratorType, types.CodeType,
+ types.FileType, types.TracebackType, types.FrameType,
+ types.BufferType, types.NotImplementedType]:
+ if isinstance(obj, typespec):
+ return False
+ return True
+
+ def getParent(self):
+ return getParent(self.obj)
+
+ def getPythonPath(self):
+ return utilities.getPythonPath(self.obj)
+
+ def getDirectlyProvidedInterfaces(self):
+ # This comes from apidoc...
+ # Getting the directly provided interfaces works only on naked objects
+ naked = removeSecurityProxy(self.obj)
+ return [utilities.getPythonPath(iface)
+ for iface in zope.interface.directlyProvidedBy(naked)]
+
+ def getProvidedInterfaces(self):
+ return self.__interfaces
+
+ def getBases(self):
+ """Get base classes.
+ """
+ klass = getattr(self.obj, '__class__', None)
+ return getattr(klass, '__bases__', None)
+
+ def getAttributes(self):
+ """Get all attributes of an object.
+
+ See objectinfo.txt to learn more.
+ """
+ klass = removeSecurityProxy(self.__klass)
+ obj = removeSecurityProxy(self.obj)
+ for name in dir(obj):
+ value = getattr(obj, name, None)
+ if value is None:
+ continue
+ if inspect.ismethod(value) or inspect.ismethoddescriptor(value):
+ continue
+ entry = {
+ 'name': name,
+ 'value': `value`,
+ 'value_linkable': self.isLinkable(value),
+ 'type' : type(value),
+ # type_link is a very browser oriented data
+ # element. Move it to a view?
+ 'type_link': self.getTypeLink(type(value)),
+ 'interface': utilities.getInterfaceForAttribute(
+ name, klass=klass)
+ }
+ entry.update(utilities.getPermissionIds(
+ name, getCheckerForInstancesOf(klass)))
+ yield entry
+
+ def getMethods(self):
+ """Get all methods of an object.
+
+ Get a list of dicts, describing the methods of an object. The
+ dicts support keys ``name`` (name of method), ``signature``,
+ ``doc`` (method's docstring or ``None``) and ``interface``
+ (the interface, where the method was defined or ``None``).
+ """
+ klass = removeSecurityProxy(self.__klass)
+ obj = removeSecurityProxy(self.obj)
+ for name in dir(obj):
+ value = getattr(obj, name, None)
+ if value is None:
+ continue
+ if not (inspect.ismethod(value)
+ and not inspect.ismethoddescriptor(value)):
+ continue
+ if inspect.ismethod(value):
+ signature = utilities.getFunctionSignature(value)
+ else:
+ signature = '(...)'
+
+ entry = {
+ 'name': name,
+ 'signature' : signature,
+ 'doc' : getattr(value, '__doc__', None),
+ 'interface': utilities.getInterfaceForAttribute(
+ name, klass=klass),
+ 'value_linkable': self.isLinkable(value),
+ }
+ entry.update(utilities.getPermissionIds(
+ name, getCheckerForInstancesOf(klass)))
+ yield entry
+
+ def isSequence(self):
+ """Is the object observed a sequence?
+ """
+ if isinstance(self.obj, types.ListType):
+ return True
+ if isinstance(self.obj, types.TupleType):
+ return True
+ return IExtendedReadSequence.providedBy(self.obj)
+
+ def getSequenceItems(self):
+ """Get the items of a sequence.
+
+ Returns a list of dicts, each representing one element of the
+ sequence.
+ """
+ if not self.isSequence():
+ return
+
+ elems = []
+ naked = removeSecurityProxy(self.obj)
+ for index in xrange(0, len(self.obj)):
+ value = naked[index]
+ elems.append({
+ 'index' : index,
+ 'value' : value,
+ 'value_type' : type(value),
+ 'value_type_link' : self.getTypeLink(type(value)),
+ 'value_linkable': self.isLinkable(value),
+ })
+ return elems
+
+ def isMapping(self):
+ """Is the object observed a mapping?
+
+ Mappings are those objects, which are dicts or provide
+ IEnumerableMapping.
+ """
+ if isinstance(self.obj, types.DictType):
+ return True
+ return IEnumerableMapping.providedBy(self.obj)
+
+ def getMappingItems(self):
+ """Get the elements of a mapping.
+
+ The elements are delivered as a list of dicts, each dict
+ containing the keys ``key``, ``key_string`` (the key as a
+ string), ``value``, ``value_type`` and ``value_type_link``.
+ """
+ elems = []
+ naked = removeSecurityProxy(self.obj)
+ if not hasattr(naked, 'items'):
+ return []
+ for key, value in naked.items():
+ elems.append({
+ 'key' : key,
+ 'key_string' : `key`,
+ 'value' : value,
+ 'value_type' : type(value),
+ 'value_type_link' : self.getTypeLink(type(value)),
+ 'value_linkable': self.isLinkable(value),
+ })
+ return elems
+
+ def isAnnotatable(self):
+ """Does the object observed expect to be annotated?
+ """
+ return IAnnotatable.providedBy(self.obj)
+
+
+ def getAnnotationsInfo(self):
+ """Get all annotations associated with an object.
+
+ If no annotations are associated with the object, ``None`` is
+ returned. Otherwise we get a list of dicts, each containing
+ keys ``key``, ``key_string`` (textual representation of key),
+ ``value``, ``value_type`` and ``value_type_link''.
+ """
+ if not self.isAnnotatable():
+ return []
+ naked = removeSecurityProxy(self.obj)
+ annotations = IAnnotations(naked)
+ if not hasattr(annotations, 'items'):
+ return []
+ elems = []
+ for key, value in annotations.items():
+ elems.append({
+ 'key' : key,
+ 'key_string' : `key`,
+ 'value' : value,
+ 'value_type' : type(value),
+ 'value_type_link' : self.getTypeLink(type(value)),
+ 'value_linkable': self.isLinkable(value),
+ 'obj' : annotations[key]
+ })
+ return elems
+
+ def getId(self):
+ """Try to determine some kind of name.
+ """
+ return (getattr(self.obj, '__name__', None)
+ or getattr(self.obj, 'id', None)
+ or getattr(self.obj, '_o_id', None))
+
+
+from zope.traversing.interfaces import ITraversable
+from zope.app.folder.interfaces import IRootFolder
+from zope.location import LocationProxy
+class AnnotationsTraverser(grok.Traverser):
+ """If first URL element is '++anno++', handle over to
+ ObjectBrowser.
+
+ This traverser binds to the RootFolder, which means, it is only
+ asked, when the publisher looks for elements in the Zope root (or
+ another IRootFolder). The further traversing is done by the Docs'
+ own traverser in it's model. See method `traverse()` in DocGrok.
+ """
+ grok.context(Interface)
+ #grok.name('anno')
+ #grok.provides(ITraversable)
+
+ def traverse(self,path):
+ namespace = 'anno'
+ print "TRAVERSE", path
+ if path.startswith(namespace):
+ name = path[len(namespace):]
+ naked = removeSecurityProxy(self.context)
+ annotations = IAnnotations(naked)
+ print annotations.items()
+ #obj = name and annotations[name] or annotations
+ #obj = path and annotations[name] or annotations
+ obj = ObjectInfo("Hello")
+ if not IPhysicallyLocatable(obj, False):
+ #obj = LocationProxy(
+ # obj, self.context, namespace + name)
+ obj = LocationProxy(
+ obj, self.context, 'anno' + name)
+ return obj
+ return
+
+ def render(self):
+ pass
+
+##
+## This comes from Dieter Maurer:
+##
+
+def determineClass(o):
+ return hasattr(o, '__class__') and o.__class__ or type(o)
+
+class Recorder(object):
+ def __init__(self): self._account = {}
+ def __call__(self, o):
+ a = self._account
+ class_ = determineClass(o)
+ if class_ in a: a[class_] += 1
+ else: a[class_] = 1
+ def account(self): return self._account
+
+def sortRecords(recorder):
+ return sorted(recorder.account().items(), key=lambda r: r[1], reverse=True)
+
+def fix((ty, no)):
+ '''some types apparently have a broken 'str'. Fix this.'''
+ try: tyn = str(ty)
+ except TypeError:
+ try: tyn = 'BROKEN-STR: %r' % ty
+ except TypeError: tyn = 'BROKEN-STR-AND-REPR'
+ return tyn, no
+
+def analyseObjects(limit=100):
+ '''analyses live objects and garbage.
+
+ The result is a pair with information for live and garbage objects, respectively.
+ The information is a dict with keys 'count' and 'sorted'.
+ 'count' is the total number of objects, 'sorted' a list with pairs
+ 'type' and 'instanceCount', inverse sorted by 'instanceCount'.
+ '''
+ result = []
+ import gc
+ for objs in gc.get_objects(), gc.garbage:
+ r = Recorder()
+ for o in objs: r(o)
+ result.append({
+ 'count':len(objs),
+ 'sorted':map(fix, sortRecords(r)[:limit]),
+ })
+ return result
+
+
+
+
+
+
+
+
+
+
+
+
+class ObjectInfo_obsolete(object):
+ """A wrapper to provide object specific information.
+
+ This is the base wrapper.
+
+ See inspect.txt to learn more about this kind of thing.
+ """
+
+ def __init__(self, obj):
+ self.obj = obj
+
+ def getName(self):
+ """Try to determine the name of an object.
+ """
+ for key in ['__name__', 'id', 'title', '_o_id']:
+ name = getattr(self.obj, key, None)
+ if name is not None:
+ break
+ return name
+
+ def getDoc(self):
+ """Fetch any doc strings from the wrapped object.
+ """
+ if hasattr(self.obj, '__class__'):
+ return getattr(self.obj.__class__, '__doc__', None)
+ return
+
+ def getType(self):
+ """Get the wrapped objects' type as a string in dotted path
+ notation.
+ """
+ return type(self.obj)
+
+ def getParent(self):
+ """Get the parent of the wrapped object.
+
+ This might result in a `TypeError` if the wrapped object is
+ not locatable in sense of ILocation, i.e. if it doesn't
+ provide ``__parent__`` attribute.
+ """
+ return getParent(self.obj)
+
+ def getChildren(self):
+ """Returns a list of children objects.
+ """
+ return dir(self.obj)
+
+ def getRoot(self):
+ """Get the root obj of the wrapped object.
+ """
+ try:
+ return getRoot(self.obj)
+ except TypeError:
+ tmp = self.obj
+ while getattr(tmp, '__parent__', None):
+ tmp = tmp.__parent__
+ return tmp
+ return
+
+ def isRoot(self):
+ """Check, whether the wrapped object is the root within its branch.
+
+ This does of course not necessarily mean, it is also the root
+ of the whole instance.
+ """
+ try:
+ return self.getRoot() == self.obj
+ except TypeError:
+ if getattr(self.obj, '__parent__', True) is None:
+ return True
+ return False
+
+ def is_broken(self):
+ """Check, whether the wrapped object is broken.
+ """
+ # XXX to be implemented.
+ return
+
+
+class DCObjectInfo(ObjectInfo_obsolete):
+ """An object wrapper, that provides DublinCore related information.
+
+ If such information is not available (for instance, because the
+ type of object can't be adapted), None is returned for every
+ value. It is therefore safe to use this info type also for objects
+ not supporting DC.
+ """
+ obj = None
+ supports_dc = False
+ _metadata = None
+ _covered_keys = [ 'created', 'modified', 'effective', 'expires',
+ 'publisher', 'creators', 'subjects', 'contributors',
+ 'description', 'title']
+
+
+ def __init__(self, obj):
+
+ super(ObjectInfo, self).__init__(obj)
+ self.obj = obj
+ try:
+ dc = IZopeDublinCore(self.obj)
+ self._metadata = dict((field, getattr(dc, field))
+ for field in getFields(IZopeDublinCore) if hasattr(dc, field))
+ self.supports_dc = True
+ except TypeError:
+ # A type error occurs, when obj can't be adapted to
+ # IZopeDublinCore.
+ self._metadata = dict([(x,None) for x in self._covered_keys])
+
+
+ def _getDCEntry(self, category):
+ if category in self._metadata.keys() and self._metadata[category]:
+ return self._metadata[category]
+ return
+
+
+
+ def getDCMisc(self):
+ """Get dict of DC metadata not covered by other methods.
+ """
+ return dict([x for x in self._metadata.items()
+ if x[0] not in self.covered_keys])
+
+ def getDCTitle(self):
+ return self._getDCEntry('title') or u''
+
+ def getDCDescription(self):
+ return self._getDCEntry('description') or u''
+
+ def getDCCreators(self):
+ return self._getDCEntry('creators') or ()
+
+ def getDCContributors(self):
+ return self._getDCEntry('contributors') or ()
+
+ def getDCSubjects(self):
+ return self._getDCEntry('subjects') or ()
+
+ def getDCPublisher(self):
+ return self._getDCEntry('publisher') or u''
+
+ def getDCCreationDate(self):
+ return self._getDCEntry('created') or u''
+
+ def getDCModificationDate(self):
+ return self._getDCEntry('modified') or u''
+
+ def getDCEffectiveDate(self):
+ return self._getDCEntry('effective') or u''
+
+ def getDCExpiringDate(self):
+ return self._getDCEntry('expires') or u''
+
Added: Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.txt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.txt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.txt 2007-08-06 15:00:38 UTC (rev 78628)
@@ -0,0 +1,650 @@
+============
+Object Infos
+============
+
+Retrieve information about objects.
+
+ObjectInfos are used by the Grok object browser to retrieve the data
+displayed in the ``inspect.html`` view. See inspect.txt to learn more
+about using the inspector.
+
+.. contents::
+
+
+ObjectInfo:
+-----------
+
+Object infos are in fact wrappers for the functionality offered by the
+Python standard library ``inspect``.
+
+Let's create a simple class:
+
+ >>> class Mammoth(object):
+ ... """A mammoth."""
+ ... pass
+
+Now create an object. Meet Manfred:
+
+ >>> manfred = Mammoth()
+ >>> manfred
+ <Mammoth object at ...>
+
+An ObjectInfo's job is it, to find out as much as possible about
+Manfred. We create an ObjectInfo by giving the object to examine as
+parameter:
+
+ >>> from grok.admin.objectinfo import ObjectInfo
+ >>> info = ObjectInfo(manfred)
+ >>> info
+ <grok.admin.objectinfo.ObjectInfo object at ...>
+
+What can we know about Manfred now?
+
+ >>> members = info.getmembers()
+ >>> ('__class__', Mammoth) in members
+ True
+
+ >>> info.getmoduleinfo() is None
+ True
+
+A Mammoth is not a module. Let's see, what happens when we examine a
+real module:
+
+ >>> import grok.admin.objectinfo
+ >>> ObjectInfo(grok.admin).getmoduleinfo()
+ ('__init__', '.pyc', 'rb', 2)
+
+ >>> info.getmodulename() is None
+ True
+
+ >>> ObjectInfo(grok.admin.objectinfo).getmodulename()
+ 'objectinfo'
+
+ >>> info.isclass()
+ False
+
+manfred is not a class, but an instance of a class. This information
+is correct.
+
+ >>> ObjectInfo(Mammoth).isclass()
+ True
+
+ >>> info.ismethod()
+ False
+
+ >>> ObjectInfo(info.ismethod).ismethod()
+ True
+
+ >>> info.isfunction()
+ False
+
+ >>> def f():
+ ... pass
+
+ >>> ObjectInfo(f).isfunction()
+ True
+
+ >>> info.istraceback()
+ False
+
+ >>> info.isframe()
+ False
+
+ >>> info.iscode()
+ False
+
+ >>> info.isbuiltin()
+ False
+
+ >>> ObjectInfo(dir).isbuiltin()
+ True
+
+ >>> info.isroutine()
+ False
+
+ >>> info.ismethoddescriptor()
+ False
+
+ >>> info.isdatadescriptor()
+ False
+
+ >>> info.getdoc()
+ 'A mammoth.'
+
+ >>> info.getcomments() is None
+ True
+
+We have a comment in the sources of ObjectInfo:
+
+ >>> ObjectInfo(grok.admin.objectinfo.ObjectInfo.getdoc).getcomments()
+ '# Source code related...\n'
+
+A bug in ``inspect.getcomments()`` causes objects with regular
+expression chars ('+', '*', etc.) in their name to fail. A workaround
+should check this and give ``None`` instead:
+
+ >>> evil_obj = Mammoth()
+ >>> evil_obj.__str__ = '+++evil+++'
+ >>> ObjectInfo(evil_obj).getcomments() is None
+ True
+
+``getfile()`` gives the name of the file the object was defined
+in. Contrary to the inspect method, this one returns None, if the
+object is a built-in module, class or function.
+
+ >>> info.getfile() is None
+ True
+
+The filename can be a compiled file ('.pyc') or a Python source file
+('.py'):
+
+ >>> filename = ObjectInfo(grok.admin.objectinfo.ObjectInfo).getfile()
+ >>> filename = filename[-1] == 'c' and filename or filename + 'c'
+ >>> filename
+ '.../grok/admin/objectinfo.pyc'
+
+
+ >>> info.getmodule()
+ <module '__builtin__' (built-in)>
+
+
+Here the same applies as for getfile().
+
+ >>> info.getsourcefile() is None
+ True
+
+ >>> ObjectInfo(grok.admin.objectinfo.ObjectInfo).getsourcefile()
+ '.../grok/admin/objectinfo.py'
+
+ >>> info.getsourcelines() is None
+ True
+
+ >>> ObjectInfo(grok.admin.objectinfo.ObjectInfo.isclass).getsourcelines()
+ ([' def isclass(self):\n',...)
+
+ >>> ObjectInfo(len).getsourcefile() is None
+ True
+
+ >>> info.getsource() is None
+ True
+
+ >>> ObjectInfo(grok.admin.objectinfo.ObjectInfo.isclass).getsource()
+ ' def isclass(self):\n...'
+
+A bug in ``inspect.getsource()`` causes objects with regular
+expression chars ('+', '*', etc.) in their name to fail. This is true
+for objects like '+++etc++site' and friends. A workaround should check
+this and give ``None`` instead:
+
+ >>> evil_obj = Mammoth()
+ >>> evil_obj.__str__ = '+++evil+++'
+ >>> ObjectInfo(evil_obj).getsource() is None
+ True
+
+
+ZopeObjectInfo
+--------------
+
+All information, which is special for Zope objects, can be digged
+using ``ZopeObjectInfo``. Zope specific are especially things related
+to the ZODB or interfaces.
+
+
+Preliminaries
++++++++++++++
+
+We first setup some objects, that we can examine thereafter. Let's
+create a ZopeObjectInfo for a typical Zope object, the root
+folder. First get the root folder:
+
+ >>> from zope.app.folder import rootFolder
+ >>> root = rootFolder()
+ >>> root
+ <zope.app.folder.folder.Folder object at ...>
+
+Then get an associated ZopeObjectInfo:
+
+ >>> from grok.admin.objectinfo import ZopeObjectInfo
+ >>> root_info = ZopeObjectInfo(root)
+ >>> root_info
+ <grok.admin.objectinfo.ZopeObjectInfo object at ...>
+
+We create a folder in the root:
+
+ >>> from zope.app.folder.folder import Folder
+ >>> subfolder = Folder()
+ >>> root['Savannah'] = subfolder
+
+and get a ZopeObjectInfo for it as well:
+
+ >>> subfolder_info = ZopeObjectInfo(subfolder)
+ >>> subfolder_info
+ <grok.admin.objectinfo.ZopeObjectInfo object at ...>
+
+Finally, we create some content in the subfolder. A cave, where
+Manfred can stay
+
+ >>> class Cave(grok.Container):
+ ... """A home, sweet home."""
+ ... pass
+
+ >>> import grok
+ >>> sweethome = Cave()
+ >>> subfolder['SweetHome'] = sweethome
+ >>> sweethome_info = ZopeObjectInfo(sweethome)
+ >>> sweethome_info
+ <grok.admin.objectinfo.ZopeObjectInfo object at ...>
+
+and put Manfred into the cave:
+
+ >>> sweethome['Owner'] = manfred
+ >>> manfred_info = ZopeObjectInfo(manfred)
+ >>> manfred_info
+ <grok.admin.objectinfo.ZopeObjectInfo object at ...>
+
+Now we can examine the objects further.
+
+getId()
++++++++
+
+Objects' names can be stored in a variety of attributes. It is
+therefore a bit difficult to get the right name (if any). ``getId``
+looks currently for ``__name__``, ``id`` and ``_o_id``. If all fails,
+``None`` is returned.
+
+ >>> root_info.getId() is None
+ True
+
+ >>> subfolder_info.getId()
+ u'Savannah'
+
+ >>> manfred_info.getId() is None
+ True
+
+getParent()
++++++++++++
+
+What is the parent object of the
+root?
+
+ >>> root_info.getParent() is None
+ True
+
+ >>> subfolder_info.getParent()
+ <zope.app.folder.folder.Folder object at ...>
+
+Oh, a folder. Is it the root?
+
+ >>> subfolder_info.getParent() == root
+ True
+
+What about the sweet home?
+
+ >>> sweethome_info.getParent()
+ <zope.app.folder.folder.Folder object at ...>
+
+ >>> sweethome_info.getParent() == subfolder
+ True
+
+Last not least, Manfred:
+
+ >>> manfred_info.getParent()
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Not enough context information to get parent', <Mammoth object at ...>)
+
+Oops! Well, mammoths are not too smart. They wander around all the
+time and so sometimes they forget where they are. Technically,
+Mammoths are not locatable in sense of ILocatable. We can make them
+locatable setting two attributes:
+
+ >>> manfred.__parent__ = sweethome
+ >>> manfred.__name__ = u'Manfred'
+
+Afterwards we can tell, where Manfred lives:
+
+ >>> manfred_info.getParent()
+ <Cave object at ...>
+
+
+getDirectlyProvidedInterfaces()
++++++++++++++++++++++++++++++++
+
+Gives a list of interfaces provided *directly* by the objects in
+dotted-path notation.
+
+ >>> root_info.getDirectlyProvidedInterfaces()
+ ['zope.app.folder.interfaces.IRootFolder']
+
+ >>> subfolder_info.getDirectlyProvidedInterfaces()
+ []
+
+ >>> sweethome_info.getDirectlyProvidedInterfaces()
+ []
+
+ >>> manfred_info.getDirectlyProvidedInterfaces()
+ []
+
+
+getInterfaces()
++++++++++++++++
+
+Gives a tuple of all interfaces provided by the object, not only the
+directly provided ones.
+
+ >>> root_info.getProvidedInterfaces()
+ (<InterfaceClass zope.app.folder.interfaces.IFolder>, <InterfaceClass persistent.interfaces.IPersistent>, <InterfaceClass zope.app.component.interfaces.IPossibleSite>, <InterfaceClass zope.app.container.interfaces.IContained>)
+
+ >>> sweethome_info.getProvidedInterfaces()
+ (<InterfaceClass zope.annotation.interfaces.IAttributeAnnotatable>, <InterfaceClass zope.app.container.interfaces.IContainer>, <InterfaceClass zope.app.container.interfaces.IContained>, <InterfaceClass persistent.interfaces.IPersistent>)
+
+Manfred again, is a bit too plain to give us interesting information:
+
+ >>> manfred_info.getProvidedInterfaces()
+ ()
+
+
+getBases()
+++++++++++
+
+What bases built our objects?
+
+ >>> root_info.getBases()
+ (<type 'persistent.Persistent'>, <class 'zope.app.component.site.SiteManagerContainer'>, <class 'zope.app.container.contained.Contained'>)
+
+ >>> sweethome_info.getBases()
+ (<class 'grok.components.Container'>,)
+
+ >>> manfred_info.getBases()
+ (<type 'object'>,)
+
+
+getAttributes()
++++++++++++++++
+
+Attributes are members, which are not methods nor methoddescriptors
+(according to ``inspect`` package). This method gives (contrary to the
+apidoc method with same name) *all* attributes, also the 'private'
+ones, because some private attributes can be of interest, when
+debugging happens.
+
+Attributes are given as an iterator over dicts, each dict containing
+the keys
+
+- ``name``: name of attribute.
+
+- ``value``: value of attribute as string.
+
+- ``value_linkable``:
+
+ a boolean indicating, whether the attribute is reachable directly,
+ for instance calling a special URL. Linkable values can be examined
+ further by calling the current URL and inserting the name of
+ attribute. Example: an object examined by
+
+ http://localhost:8080/someobj/inspect.html
+
+ which owns a linkable attribute ``myattr`` can be examined by
+
+ http://localhost:8080/someobj/myattr/inspect.html
+
+- ``type``: type of value as string.
+
+- ``type_link``: link to the type documentation as str.
+
+- ``interface``: name of the interface, this attribute was defined in
+ or ``None``.
+
+- ``read_perm`` and ``write_perm``: permissions to read/write the
+ attribute.
+
+To get the attributes as a list, you can use ``list()`` as shown
+below.
+
+We now look for the attributes of the root folder:
+
+ >>> attrs = list(root_info.getAttributes())
+
+There should be a ``data`` attribute available:
+
+ >>> attr_names = [x['name'] for x in attrs]
+ >>> 'data' in attr_names
+ True
+
+And the ``data`` attribute should have eight entries (as described
+above):
+
+ >>> data_attr = [x for x in attrs if x['name'] == 'data'][0]
+ >>> len(data_attr)
+ 8
+
+First, let's determine the name of the attribute:
+
+ >>> data_attr['name']
+ 'data'
+
+The value of the attribute:
+
+ >>> data_attr['value']
+ '<BTrees.OOBTree.OOBTree object at ...>'
+
+The value is directly reachable using the traverser:
+
+ >>> data_attr['value_linkable']
+ True
+
+We can get some information about the type of the value:
+
+ >>> data_attr['type']
+ <type 'BTrees.OOBTree.OOBTree'>
+
+ >>> data_attr['type_link']
+ 'BTrees/OOBTree/OOBTree'
+
+Is there an interface this attribute was defined in?
+
+ >>> data_attr['interface'] is None
+ True
+
+There are no special permissions defined to read or write the 'data'
+attribute.
+
+ >>> data_attr['read_perm'] is None
+ True
+
+ >>> data_attr['write_perm'] is None
+ True
+
+
+getMethods()
+++++++++++++
+
+This method gives (contrary to the apidoc method with same name) *all*
+methods, also the 'private' ones, because some private attributes can
+be of interest, when debugging happens.
+
+Methods are given as an iterator over dicts, each dict containing
+the keys
+
+- ``name``: the name of the method.
+
+- ``signature``: the signature of the methods as string.
+
+- ``doc``: an doc string of the method (if one exists)
+
+- ``interface``: the interface this method was implemented at (if
+ any).
+
+- ``read_perm`` and ``write_perm``: permissions to read/write the
+ method.
+
+We first get the methods of the root object. Because ``getMethods``
+returns an iterable, we form a list, using ``list()``:
+
+ >>> methods = list(root_info.getMethods())
+
+ >>> len(methods)
+ 13
+
+Okay, there are 13 methods defined in this object. We pick one to
+examine it further:
+
+ >>> items_method = [x for x in methods if x['name'] == 'items'][0]
+ >>> len(items_method)
+ 7
+
+There are six keys in the dict describing the ``items()`` method of
+the root folder.
+
+ >>> items_method['name']
+ 'items'
+
+The ``items()`` method takes no parameters:
+
+ >>> items_method['signature']
+ '()'
+
+The method is documented:
+
+ >>> items_method['doc']
+ 'Return a sequence-like object containing tuples of the form\n (name, object) for the objects that appear in the folder.\n '
+
+This is the raw documentation string, obviously. It can be formatted
+using render methods defined in the inspect view class.
+
+The method has no special interface where it was defined:
+
+ >>> items_method['interface'] is None
+ True
+
+And there are no special permissions set on this method:
+
+ >>> items_method['read_perm'] is None
+ True
+
+ >>> items_method['write_perm'] is None
+ True
+
+
+isSequence()
+++++++++++++
+
+Sequences are those objects, which provide the IExtendedReadSequence
+interface, are of tuple type or of list type.
+
+ >>> root_info.isSequence()
+ False
+
+ >>> ZopeObjectInfo(['a', 'list', 'of', 'values']).isSequence()
+ True
+
+ >>> ZopeObjectInfo(('a', 'tuple', 'of', 'values')).isSequence()
+ True
+
+ >>> ZopeObjectInfo({'a': 'dict', 'of': 'values'}).isSequence()
+ False
+
+
+getSequenceItems()
+++++++++++++++++++
+
+ >>> testlist = ['a', 'list', 'of', 'values']
+ >>> list_info = ZopeObjectInfo(testlist).getSequenceItems()
+ >>> len(list_info)
+ 4
+
+ >>> first_elem = list_info[0]
+ >>> first_elem['index']
+ 0
+
+ >>> first_elem['value_type']
+ <type 'str'>
+
+ >>> first_elem['value_type_link']
+ '__builtin__/str'
+
+ >>> first_elem['value']
+ 'a'
+
+
+isMapping()
++++++++++++
+
+ >>> root_info.isMapping()
+ True
+
+ >>> ZopeObjectInfo(['a', 'list', 'of', 'values']).isMapping()
+ False
+
+ >>> ZopeObjectInfo(('a', 'tuple', 'of', 'values')).isMapping()
+ False
+
+ >>> ZopeObjectInfo({'a': 'dict', 'of': 'values'}).isMapping()
+ True
+
+ >>> ZopeObjectInfo(root.data).isMapping()
+ False
+
+
+getMappingItems()
++++++++++++++++++
+
+ >>> map_elems = root_info.getMappingItems()
+ >>> u'Savannah' in [x['key'] for x in map_elems]
+ True
+
+ >>> map_elem = [x for x in map_elems if x['key'] == u'Savannah'][0]
+ >>> map_elem['key']
+ u'Savannah'
+
+ >>> map_elem['key_string']
+ "u'Savannah'"
+
+ >>> map_elem['value']
+ <zope.app.folder.folder.Folder object at ...>
+
+ >>> map_elem['value_type']
+ <class 'zope.app.folder.folder.Folder'>
+
+ >>> map_elem['value_type_link']
+ 'zope/app/folder/folder/Folder'
+
+Objects, which are not mappings, should return the empty list:
+
+ >>> ZopeObjectInfo('a string').getMappingItems()
+ []
+
+isAnnotatable()
++++++++++++++++
+
+Checks for the interface IAnnotatable. Most 'usual' Zope objects are
+annotatable...
+
+ >>> root_info.isAnnotatable()
+ True
+
+ >>> sweethome_info.isAnnotatable()
+ True
+
+...but some very simple ones are not (or have not declared to be
+annotatable although they are):
+
+ >>> manfred_info.isAnnotatable()
+ False
+
+
+getAnnotationsInfo()
+++++++++++++++++++++
+
+ >>> root_info.getAnnotationsInfo()
+ []
+
+ >>> sweethome_info.getAnnotationsInfo()
+ []
+
+Manfred is not annotatable, but instead of an error we get an empty
+list:
+
+ >>> manfred_info.getAnnotationsInfo()
+ []
+
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/static/grok.css
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/static/grok.css 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/static/grok.css 2007-08-06 15:00:38 UTC (rev 78628)
@@ -208,6 +208,10 @@
margin-bottom:0.5em;
}
+.docgrok-value {
+margin-left:2em;
+}
+
.docgrok-pathvalue {
font-family:courier;
}
@@ -236,6 +240,23 @@
background-color:#fff;
}
+.docgrok-table {
+font-size:1.0em;
+empty-cells:show;
+border-collapse:separate;
+}
+.docgrok-table th {
+padding-left:3px;
+padding-right:3px;
+border:1px solid #eee;
+}
+.docgrok-table td {
+min-height:1.0em;
+padding-left:5px;
+padding-right:5px;
+
+}
+
/* --- system proces related --- */
div#server-processes {
background-color:#f9f9D4;
@@ -292,3 +313,10 @@
padding-top:1em;
padding-left:0.8em;
}
+
+#logout {
+padding:1px;
+padding-right:150px;
+background-color:#ddd;
+text-align:right;
+}
\ No newline at end of file
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/tests/test_grokadmin.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/tests/test_grokadmin.py 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/tests/test_grokadmin.py 2007-08-06 15:00:38 UTC (rev 78628)
@@ -2,9 +2,11 @@
from pkg_resources import resource_listdir
from zope.testing import doctest, cleanup
import zope.component.eventtesting
+from zope.annotation.attribute import AttributeAnnotations
def setUpZope(test):
zope.component.eventtesting.setUp(test)
+ zope.component.provideAdapter(AttributeAnnotations)
def cleanUpZope(test):
cleanup.cleanUp()
@@ -34,7 +36,7 @@
suite = unittest.TestSuite()
for name in []:
suite.addTest(suiteFromPackage(name))
- for name in ['docgrok.txt']:
+ for name in ['docgrok.txt','objectinfo.txt', 'utilities.py']:
suite.addTest(doctest.DocFileSuite(name,
package='grok.admin',
setUp=setUpZope,
Added: Sandbox/ulif/grok-adminui/src/grok/admin/utilities.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/utilities.py (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/utilities.py 2007-08-06 15:00:38 UTC (rev 78628)
@@ -0,0 +1,240 @@
+"""Helper functions for grok admin.
+"""
+import re
+from zope.tal.taldefs import attrEscape
+from urlparse import urlparse, urlunparse
+
+def getPathLinksForObject(obj, root_url=''):
+ """Given an object, this function returns HTML code with links to
+ documentation.
+
+ The object must provide a string representation like 'foo.blah
+ object at 0x9999999'. Returned is then a string, where 'foo' and
+ 'blah' are embedded in HTML links to docgrok documentation for foo
+ and foo.blah.
+
+ The (optional) ``root_url`` is used to create the links to docgrok
+ documentation. It is expected to be the URL, which can generate
+ docgrok documentation by appending '/docgrok' to the URL.
+
+ We can use ObjectInfo objects to check this:
+
+ >>> from grok.admin.objectinfo import ObjectInfo
+ >>> obj = ObjectInfo(None)
+ >>> obj
+ <grok.admin.objectinfo.ObjectInfo object at ...>
+
+ Obviously we have a string representation of the required form
+ here. So we can get HTML with links to the documentation for
+ ``grok``, ``grok.admin`` and so on.
+
+ >>> from grok.admin.utilities import getPathLinksForObject
+ >>> link = getPathLinksForObject(obj)
+ >>> link
+ "<<a href='/docgrok/grok/'>grok</a>... object at ..."
+
+ We got a link to the ``grok`` documentation. Also links to
+ ``grok.admin``, ``grok.admin.objectinfo`` and
+ ``grok.admin.objectinfo.ObjectInfo`` are provided:
+
+ >>> link
+ "<...<a href='/docgrok/grok/admin/'>admin</a>... object at ..."
+
+ If we provide a root_url, we will find it in the links:
+
+ >>> link = getPathLinksForObject(obj, 'http://localhost:8080')
+ >>> link
+ "<<a href='http://localhost:8080/docgrok/grok/'>grok</a>..."
+
+ If no dotted path is included in objects strings representation, a
+ simple string without links is returned:
+
+ >>> getPathLinksForObject(None)
+ "'None'"
+
+ HTML entities should be encoded. We set up a site-manager to get
+ an 'illegal' object representation including regular expression
+ chars ('+') and no dotted path:
+
+ >>> from zope.app.folder import rootFolder
+ >>> root = rootFolder()
+ >>> from zope.app.component import site
+ >>> sm = site.LocalSiteManager(root)
+ >>> root.setSiteManager(sm)
+ >>> sm
+ <LocalSiteManager ++etc++site>
+
+ This is a strange object identifier. Anyway:
+
+ >>> getPathLinksForObject(sm)
+ "'<LocalSiteManager ++etc++site>'"
+
+ """
+ r_exp = re.compile("'<(.+)( object at .*)>'")
+
+ raw = `str(obj)`
+ match = r_exp.match(raw)
+ if match is None:
+ return attrEscape(raw)
+
+ result = "<"
+ url = root_url + '/docgrok/'
+ for part in match.group(1).split('.'):
+ url = url + part + '/'
+ result += "<a href='%s'>%s</a>." % (url, part)
+ if len(result) and result[-1] == '.':
+ result = "%s%s>" % (result[:-1], match.group(2))
+ return result
+ return raw
+
+def getPathLinksForClass(klass, root_url=''):
+ """Given a class or classlike object, this function returns HTML
+ code with links to documentation.
+
+ The klass object must provide a string representation like '<class
+ foo.Bar>'. Returned is then a string, where 'foo' and
+ 'Bar' are embedded in HTML links to docgrok documentation for foo
+ and foo.Bar.
+
+ The (optional) ``root_url`` is used to create the links to docgrok
+ documentation. It is expected to be the URL, which can generate
+ docgrok documentation by appending '/docgrok' to the URL.
+
+ We can use class ObjectInfo to check this:
+
+ >>> from grok.admin.objectinfo import ObjectInfo
+ >>> ObjectInfo
+ <class 'grok.admin.objectinfo.ObjectInfo'>
+
+ >>> from grok.admin.utilities import getPathLinksForClass
+ >>> htmlcode = getPathLinksForClass(ObjectInfo)
+ >>> htmlcode
+ "<class '<a href='/docgrok/grok/'>grok</a>...'>"
+
+ When we provide a root_url the link will include it in the
+ href-attribute:
+
+ >>> getPathLinksForClass(ObjectInfo, 'http://localhost')
+ "<class '<a href='http://localhost/docgrok/grok/'>grok</a>...'>"
+
+ If the class does not provide an appropriate string
+ representation, we will get the representation without any links:
+
+ >>> getPathLinksForClass(None, 'http://localhost')
+ "'None'"
+
+ This also works with 'class-like' objects, for instance interfaces
+ and their interface-classes:
+
+ >>> from zope.app.folder import rootFolder
+ >>> from zope.interface import providedBy
+ >>> root = rootFolder()
+ >>> iface = list(providedBy(root))[0]
+ >>> iface
+ <InterfaceClass zope.app.folder.interfaces.IRootFolder>
+
+ >>> getPathLinksForClass(iface)
+ "<InterfaceClass '<a href='/docgrok/zope/'>zope</a>...'>"
+
+ HTML entities should be encoded. We set up a site-manager to get
+ an 'illegal' object representation including regular expression
+ chars ('+') and no dotted path:
+
+ >>> from zope.app.folder import rootFolder
+ >>> root = rootFolder()
+ >>> from zope.app.component import site
+ >>> sm = site.LocalSiteManager(root)
+ >>> root.setSiteManager(sm)
+ >>> sm
+ <LocalSiteManager ++etc++site>
+
+ This is a strange object identifier. Anyway:
+
+ >>> getPathLinksForClass(sm)
+ "<LocalSiteManager '<a href='/docgrok/++etc++site/'>...</a>'>"
+
+ """
+ r_exp = re.compile(".*<(.*) '?(.+)'?(.*)>.*")
+ raw = `str(klass)`
+ match = r_exp.match(raw)
+ if match is None:
+ return attrEscape(raw)
+
+ result = "<%s '" % (match.group(1),)
+ url = root_url + '/docgrok/'
+ for part in match.group(2).split('.'):
+ url = "%s%s/" % (url, part)
+ result += "<a href='%s'>%s</a>." % (url, part)
+ if len(result) and result[-1] == '.':
+ result = "%s'%s>" % (result[:-1], match.group(3))
+ return result
+ return raw
+
+def getPathLinksForDottedName(name, root_url=''):
+ """
+ """
+ if name is None:
+ return ''
+ result = ''
+ url = root_url + '/docgrok/'
+ for part in name.split('.'):
+ url = "%s%s/" % (url, part)
+ result += "<a href='%s'>%s</a>." % (url, part)
+ if len(result) and result.endswith('.'):
+ result = result[:-1]
+ return result
+ return name
+
+def isContainingEvilRegExpChars(strval):
+ """Check whether a string contains evil chars.
+
+ 'Evil' with respect to regular expressions is a string, that
+ contains chars, with a special meaning in regular expressions.
+
+ We indeed must provide a string:
+
+ >>> from grok.admin.utilities import isContainingEvilRegExpChars
+ >>> isContainingEvilRegExpChars(None)
+ Traceback (most recent call last):
+ ...
+ TypeError: expected string or buffer
+
+ >>> isContainingEvilRegExpChars('foo')
+ False
+
+ >>> isContainingEvilRegExpChars('foo++etc++bar')
+ True
+
+ >>> isContainingEvilRegExpChars('foo*bar')
+ True
+
+ """
+ evil_chars = re.compile('.*(\*|\+|\(|\)|\{|\}).*')
+ if evil_chars.match(strval):
+ return True
+ return False
+
+
+def getParentURL(url):
+ """Compute the parent URL for an object described by URL.
+
+ >>> from grok.admin.utilities import getParentURL
+ >>> getParentURL('http://foo:8080/myobj')
+ 'http://foo:8080/'
+
+ >>> getParentURL('http://foo:8080/myfoo/mybar')
+ 'http://foo:8080/myfoo/'
+
+ We want an URL always to end with a slash:
+
+ >>> getParentURL('http://foo:8080')
+ 'http://foo:8080/'
+
+ """
+ url_list = list(urlparse(url))
+ path = url_list[2]
+ if path.endswith('/'):
+ path = path[:-1]
+ path = path.rsplit('/', 1)[0] + '/'
+ url_list[2] = path
+ return urlunparse(url_list)
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view.py 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view.py 2007-08-06 15:00:38 UTC (rev 78628)
@@ -1,22 +1,25 @@
import grok
import os
import inspect
+from urllib import urlencode
+
from grok.admin import docgrok
from grok.admin.docgrok import DocGrok, DocGrokPackage, DocGrokModule
-from grok.admin.docgrok import DocGrokClass, DocGrokInterface, DocGrokGrokApplication
-from grok.admin.docgrok import DocGrokTextFile
+from grok.admin.docgrok import DocGrokTextFile, DocGrokGrokApplication
+from grok.admin.docgrok import DocGrokClass, DocGrokInterface, getItemLink
+from grok.admin.objectinfo import ZopeObjectInfo
+from grok.admin.utilities import getPathLinksForObject, getPathLinksForClass
+from grok.admin.utilities import getPathLinksForDottedName, getParentURL
+
import zope.component
from zope.interface import Interface
-from zope.app.folder.interfaces import IRootFolder
-
from zope.app import zapi
+from zope.interface.interface import InterfaceClass
from zope.app.applicationcontrol.interfaces import IServerControl
from zope.app.applicationcontrol.applicationcontrol import applicationController
from zope.app.applicationcontrol.runtimeinfo import RuntimeInfo
from zope.app.applicationcontrol.browser.runtimeinfo import RuntimeInfoView
-
-from zope.interface.interface import InterfaceClass
from zope.app.apidoc import utilities, codemodule
from zope.app.apidoc.utilities import getPythonPath, renderText, columnize
from zope.app.apidoc.codemodule.module import Module
@@ -24,10 +27,11 @@
from zope.app.apidoc.codemodule.function import Function
from zope.app.apidoc.codemodule.text import TextFile
from zope.app.apidoc.codemodule.zcml import ZCMLFile
-
+from zope.app.folder.interfaces import IRootFolder
+from zope.app.security.interfaces import ILogout, IAuthentication
from zope.app.security.interfaces import IUnauthenticatedPrincipal
-
from zope.proxy import removeAllProxies
+from zope.tal.taldefs import attrEscape
import z3c.flashmessage.interfaces
@@ -44,7 +48,7 @@
def update(self, inspectapp=None, application=None):
if inspectapp is not None:
- self.redirect( self.url("docgrok") + "/%s/index"%(application.replace('.','/'),))
+ self.redirect(self.url("docgrok") + "/%s/index"%(application.replace('.','/'),))
return
def render(self, application, name, inspectapp=None):
@@ -72,6 +76,7 @@
items = [items]
for name in items:
del self.context[name]
+ self.flash(u'Application %s was successfully deleted.' % (name,))
self.redirect(self.url(self.context))
@@ -94,100 +99,152 @@
raise ValueError("No application nor root element found.")
def in_docgrok(self):
- return '/docgrok/' in self.url()
+ return '/docgrok/' in self.url() or 'inspect.html' in self.url()
+ def is_authenticated(self):
+ """Check, wether we are authenticated.
+ """
+ return not IUnauthenticatedPrincipal.providedBy(self.request.principal)
-def getDottedPathDict(dotted_path):
- """Get a dict containing parts of a dotted path as links.
- """
- if dotted_path is None:
- return {}
-
- result = []
- part_path = ""
- for part in dotted_path.split( '.' ):
- name = part
- if part_path != "":
- name = "." + part
- part_path += part
- result.append( {
- 'name':name,
- 'url':"/docgrok/%s" % (part_path,)
- })
- part_path += "/"
- return result
+class Macros(GAIAView):
+ """Provides the o-wrap layout."""
+ grok.context(Interface)
+
+
class Inspect(GAIAView):
"""Basic object browser.
"""
+
grok.context(Interface)
+ grok.name(u'inspect.html')
+ grok.require('grok.ManageApplications')
- def __init__(self, context, request):
- # Leave out the Introspector init, because it requires
- # ++apidoc++ to be enabled and setups skin-related stuff we
- # don't want.
- super(GAIAView, self).__init__(context,request)
+ _metadata = None
-
- def getPathParts(self,dotted_path):
- return getDottedPathDict(dotted_path)
+ def update(self, show_private=False, *args, **kw):
+ obj = self.context
+ if isinstance(self.context, ZopeObjectInfo):
+ # When the docgrok-object traverser delivers content, then
+ # we get a wrapped context: the meant object is wrapped
+ # into a ZopeObjectInfo.
+ obj = self.context.obj
+
+ self.ob_info = ZopeObjectInfo(obj)
+ ob_info = self.ob_info
+ self.show_private = show_private
+ root_url = self.root_url()
+ parent = ob_info.getParent()
+ parent = {'class_link':
+ parent and getPathLinksForObject(parent) or '',
+ 'obj_link' : getItemLink('',getParentURL(self.url(''))),
+ 'obj' : parent
+ }
+ bases = [getPathLinksForClass(x) for x in ob_info.getBases()]
+ bases.sort()
+
+ ifaces = [getPathLinksForClass(x) for x in
+ ob_info.getProvidedInterfaces()]
+ ifaces.sort()
- def getId(self):
- if hasattr( self.context, '__name__'):
- return self.context.__name__
- if hasattr( self.context, 'id' ):
- return self.context.id
- return
+ methods = [x for x in list(ob_info.getMethods())
+ if self.show_private or not x['name'].startswith('_')]
+ for method in methods:
+ if method['interface']:
+ method['interface'] = getPathLinksForDottedName(
+ method['interface'], root_url)
+ if method['doc']:
+ method['doc'] = renderText(method['doc'], getattr(obj,'__module__', None))
- def getZODBPath(self):
- # XXX To be implemented.
- return
+ attrs = [x for x in list(ob_info.getAttributes())
+ if self.show_private or not x['name'].startswith('_')
+ ]
+ for attr in attrs:
+ if '.' in str(attr['type']):
+ attr['type'] = getPathLinksForClass(attr['type'], root_url)
+ else:
+ attr['type'] = attrEscape(str(attr['type']))
+ if attr['interface']:
+ attr['interface'] = getPathLinksForDottedName(
+ attr['interface'], root_url)
+ attr['obj'] = getattr(obj, attr['name'], None)
+ attr['docgrok_link'] = getItemLink(attr['name'], self.url(''))
+ attrs.sort(lambda x,y: x['name']>y['name'])
- def getDottedPath(self):
- if hasattr(self.context, '__class__'):
- klassname = str(self.context.__class__)
- return klassname.rsplit("'", 2)[1]
- return
+ seqitems = ob_info.getSequenceItems() or []
+ for item in seqitems:
+ if '.' in str(item['value_type']):
+ item['value_type'] = getPathLinksForClass(item['value_type'],
+ root_url)
+ else:
+ item['value_type'] = attrEscape(str(item['value_type']))
+ item['obj'] = obj[item['index']]
+ item['docgrok_link'] = getItemLink(item['index'], self.url(''))
+ seqitems.sort()
+ mapitems = [x for x in ob_info.getMappingItems()
+ if self.show_private or not x['key'].startswith('_')]
+ for item in mapitems:
+ if '.' in str(item['value_type']):
+ item['value_type'] = getPathLinksForClass(item['value_type'],
+ root_url)
+ else:
+ item['value_type'] = attrEscape(str(item['value_type']))
+ item['obj'] = obj[item['key']]
+ item['docgrok_link'] = getItemLink(item['key'], self.url(''))
+ mapitems.sort(lambda x,y: x['key']>y['key'])
- def getSize(self):
- # XXX To be implemented.
- return
+ annotations = [x for x in ob_info.getAnnotationsInfo()
+ if self.show_private or not x['key'].startswith('_')]
+ for item in annotations:
+ if '.' in str(item['value_type']):
+ item['value_type'] = getPathLinksForClass(item['value_type'],
+ root_url)
+ else:
+ item['value_type'] = attrEscape(str(item['value_type']))
+ item['docgrok_link'] = getItemLink(item['key'], self.url(''))
+ annotations.sort(lambda x,y: x['key']>y['key'])
- def getCreationDate(self):
- # XXX To be implemented.
- return
+
+ self.info = {
+ 'name' : ob_info.getId() or u'<unnamed object>',
+ 'type' : getPathLinksForClass((getattr(obj,
+ '__class__',
+ None)
+ or type(obj)), root_url),
+ 'obj_link' : getPathLinksForObject(obj, root_url),
+ 'moduleinfo' : ob_info.getmoduleinfo(),
+ 'modulename' : ob_info.getmodulename(),
+ 'ismodule' : ob_info.ismodule(),
+ 'isclass' : ob_info.isclass(),
+ 'ismethod' : ob_info.ismethod(),
+ 'isfunction' : ob_info.isfunction(),
+ 'iscode' : ob_info.iscode(),
+ 'isbuiltin' : ob_info.isbuiltin(),
+ 'isroutine' : ob_info.isroutine(),
+ 'issequence' : ob_info.isSequence(),
+ 'ismapping' : ob_info.isMapping(),
+ 'isannotatable' : ob_info.isAnnotatable(),
+ 'doc' : renderText(ob_info.getdoc(),None),
+ 'comments' : ob_info.getcomments(),
+ 'module' : ob_info.getmodule(),
+ 'sourcefile' : ob_info.getsourcefile(),
+ 'source' : ob_info.getsource(),
+ 'parent' : parent,
+ 'dotted_path' : ob_info.getPythonPath(),
+ 'provided_interfaces' : ob_info.getDirectlyProvidedInterfaces(),
+ 'interfaces' : ifaces,
+ 'bases' : bases,
+ 'attributes' : attrs,
+ 'methods' : methods,
+ 'sequenceitems' : seqitems,
+ 'mappingitems' : mapitems,
+ 'annotations' : annotations
+ }
+
- def getModificationDate(self):
- # XXX To be implemented.
- return
-
- def isBroken(self):
- # XXX To be implemented.
- return
-
- def getSecurityInfo(self):
- # XXX To be implemented.
- return
-
- def getParent(self):
- if hasattr( self.context, '__parent__'):
- return self.context.__parent__
- return
-
- def getChildren(self):
- # XXX To be implemented.
- return
-
- def getType(self):
- if hasattr(self.context, '__class__'):
- klassname = str(self.context.__class__)
- return klassname.rsplit("'", 2)[1]
- return str(self.context)
-
-
class Index(GAIAView):
"""A redirector to the real frontpage."""
@@ -203,7 +260,7 @@
self.redirect(self.url('applications'))
-class loginForm(GAIAView):
+class LoginForm(GAIAView):
"""A login screen for session based authentication.
To activate loginForm, i.e. session based authentication, an
@@ -224,7 +281,19 @@
self.redirect(camefrom)
return
+class Logout(GAIAView):
+ """Log out screen."""
+ grok.name('logout')
+
+ def update(self):
+ auth = zope.component.getUtility(IAuthentication)
+ logout = ILogout(auth)
+ logout.logout(self.request)
+ pass
+
+
+
class Applications(GAIAView):
"""View for application management."""
@@ -245,7 +314,7 @@
if hasattr(x, '__class__') and x.__class__ in apps]
self.applications = (
{'name': "%s.%s" % (x.__module__, x.__name__),
- 'docurl':("%s.%s" % (x.__module__, x.__name__)).replace( '.', '/')}
+ 'docurl':("%s.%s" % (x.__module__, x.__name__)).replace('.', '/')}
for x in apps)
self.installed_applications = inst_apps
@@ -326,10 +395,26 @@
self.redirect(self.url())
-class Macros(GAIAView):
- """Provides the o-wrap layout."""
- grok.context(Interface)
+def getDottedPathDict(dotted_path):
+ """Get a dict containing parts of a dotted path as links.
+ """
+ if dotted_path is None:
+ return {}
+
+ result = []
+ part_path = ""
+ for part in dotted_path.split('.'):
+ name = part
+ if part_path != "":
+ name = "." + part
+ part_path += part
+ result.append({
+ 'name':name,
+ 'url':"/docgrok/%s" % (part_path,)
+ })
+ part_path += "/"
+ return result
class DocGrokView(GAIAView):
@@ -340,7 +425,8 @@
"""
grok.context(DocGrok)
- grok.name( 'index' )
+ grok.name('index')
+ grok.require('grok.ManageApplications')
def getDoc(self, text=None, heading_only=False):
"""Get the doc string of the module STX formatted."""
@@ -364,8 +450,8 @@
lines = [line for line in lines if not line.startswith('$Id')]
return renderText('\n'.join(lines), self.context.getPath())
- def getDocHeading( self, text=None):
- return self.getDoc( text, True)
+ def getDocHeading(self, text=None):
+ return self.getDoc(text, True)
def getPathParts(self, path=None):
"""Get parts of a dotted name as url and name parts.
@@ -377,19 +463,19 @@
return getDottedPathDict(path)
result = []
part_path = ""
- for part in path.split( '.' ):
+ for part in path.split('.'):
name = part
if part_path != "":
name = "." + part
part_path += part
- result.append( {
+ result.append({
'name':name,
'url':"/docgrok/%s" % (part_path,)
})
part_path += "/"
return result
- def getEntries( self, columns=True ):
+ def getEntries(self, columns=True):
"""Return info objects for all modules and classes in the
associated apidoc container.
@@ -451,21 +537,21 @@
"""A view for packages handled by DocGrok."""
grok.context(DocGrokPackage)
- grok.name( 'index' )
+ grok.name('index')
class DocGrokModuleView(DocGrokView):
"""A view for modules handled by DocGrok."""
grok.context(DocGrokModule)
- grok.name( 'index' )
+ grok.name('index')
class DocGrokClassView(DocGrokView):
"""A view for classes handled by DocGrok."""
grok.context(DocGrokClass)
- grok.name( 'index' )
+ grok.name('index')
def getBases(self):
return self._listClasses(self.context.apidoc.getBases())
@@ -496,12 +582,12 @@
if not fullpath:
continue
path, name = fullpath.rsplit('.', 1)
- info.append( {
+ info.append({
'path': path or None,
- 'path_parts' : self.getPathParts( path ) or None,
+ 'path_parts' : self.getPathParts(path) or None,
'name': name,
'url': fullpath and fullpath.replace('.','/') or None,
- 'doc': self.getDocHeading( cls.__doc__ ) or None
+ 'doc': self.getDocHeading(cls.__doc__) or None
})
return info
@@ -509,19 +595,19 @@
class DocGrokInterfaceView(DocGrokClassView):
grok.context(DocGrokInterface)
- grok.name( 'index' )
+ grok.name('index')
class DocGrokGrokApplicationView(DocGrokClassView):
grok.context(DocGrokGrokApplication)
- grok.name( 'index' )
+ grok.name('index')
class DocGrokTextFileView(DocGrokView):
grok.context(DocGrokTextFile)
- grok.name( 'index' )
+ grok.name('index')
def getContent(self):
lines = self.context.getContent()
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/applications.pt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/applications.pt 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/applications.pt 2007-08-06 15:00:38 UTC (rev 78628)
@@ -17,6 +17,9 @@
(<span tal:replace="app/__class__/__name__"/>)
</a>
+ [<a href=""
+ tal:attributes="href string:${context/@@absolute_url}/${app/__name__}/@@inspect.html"
+ >object browser</a>]
</div>
<p>
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokpackageview.pt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokpackageview.pt 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokpackageview.pt 2007-08-06 15:00:38 UTC (rev 78628)
@@ -37,15 +37,15 @@
</div>
</div>
- <h2>Subpackages:</h2>
+ <h2>Textfiles:</h2>
<div class="docgrok-entry" tal:repeat="item view/getEntries">
- <div tal:condition="item/ispackage">
+ <div tal:condition="item/istextfile">
<div class="docgrok-pathvalue">
- package
+
<a href=""
tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
- tal:content="string: ${context/path}.${item/name}">
+ tal:content="string: ${item/name}">
moduleName
</a>
</div>
@@ -55,21 +55,17 @@
</div>
<div class="docgrok-annotation2"
tal:condition="not: item/doc">
- You can use <span class="docgrok-pycode1">import <span
- tal:replace="string: ${context/path}.${item/name}">a.b</span>
- </span>
- to make the elements of this package available in your
- application or component.
</div>
</div>
</div>
- <h2>Modules:</h2>
+ <h2>Subpackages:</h2>
+
<div class="docgrok-entry" tal:repeat="item view/getEntries">
- <div tal:condition="item/ismodule">
+ <div tal:condition="item/ispackage">
<div class="docgrok-pathvalue">
- module
+ package
<a href=""
tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
tal:content="string: ${context/path}.${item/name}">
@@ -85,21 +81,21 @@
You can use <span class="docgrok-pycode1">import <span
tal:replace="string: ${context/path}.${item/name}">a.b</span>
</span>
- to make the elements of this module available in your
+ to make the elements of this package available in your
application or component.
</div>
</div>
</div>
- <h2>Textfiles:</h2>
+ <h2>Modules:</h2>
<div class="docgrok-entry" tal:repeat="item view/getEntries">
- <div tal:condition="item/istextfile">
+ <div tal:condition="item/ismodule">
<div class="docgrok-pathvalue">
-
+ module
<a href=""
tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
- tal:content="string: ${item/name}">
+ tal:content="string: ${context/path}.${item/name}">
moduleName
</a>
</div>
@@ -109,21 +105,15 @@
</div>
<div class="docgrok-annotation2"
tal:condition="not: item/doc">
-<!--
You can use <span class="docgrok-pycode1">import <span
tal:replace="string: ${context/path}.${item/name}">a.b</span>
</span>
- to make the elements of this package available in your
+ to make the elements of this module available in your
application or component.
--->
</div>
</div>
</div>
-<!--
- <div tal:content="context/msg" />
--->
-
</div>
</div>
</body>
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokview.pt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokview.pt 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokview.pt 2007-08-06 15:00:38 UTC (rev 78628)
@@ -8,21 +8,99 @@
<h1 >
Welcome to DocGrok...
</h1>
- <div>
- you might want to discover the following trails...
- <ul>
- <li>
+ <div class="emph">
+ DocGrok is Grok's run-time documentation system.
+
+ </div>
+
+ <h2>
+ DocGrok Package Browser
+ </h2>
+
+ <div class="docgrok-annotation1">
+ <p>
+ Use the package browser to browse the locally installed
+ Python packages, their classes, members and included text
+ documentation. You can, for example,...
+ </p>
+ <div class="docgrok-annotation1">
+ <div>
<a href=""
+ class="emph"
tal:attributes="href string:${view/root_url}/docgrok/zope">
- The zope package</a>
- </li>
- <li>
+ browse the zope package</a>
+ </div>
+ <div>
<a href=""
+ class="emph"
tal:attributes="href string:${view/root_url}/docgrok/grok">
- The Grok package</a>
- </li>
- </ul>
+ browse the grok package</a>
+ </div>
+ </div>
+ <p>
+ See
+ <a href=""
+ class="emph"
+ tal:attributes="href string:
+ ${view/root_url}/docgrok/grok/admin/docgrok.txt">
+ docgrok documentation</a> to learn more
+ about this feature of Grok.
+ </p>
</div>
+
+ <h2>
+ DocGrok Object Browser
+ </h2>
+
+ <div class="docgrok-annotation1">
+ <p>
+ The DocGrok object browser supports discovering of objects
+ available in the runtime system. You can for example
+ examine the
+ </p>
+ <div class="docgrok-annotation1">
+ <div>
+ <a href=""
+ class="emph"
+ tal:attributes="href string:${view/root_url}/@@inspect.html">
+ ZODB root folder</a>
+ </div>
+ </div>
+ <p>
+ See <a href="" class="emph"
+ tal:attributes="href string:
+ ${view/root_url}/docgrok/grok/admin/inspect.txt">object
+ browsers documentation</a> to learn more about this
+ feature of Grok.
+ </p>
+ </div>
+
+ <h2>
+ External Documentation
+ </h2>
+
+ <div class="docgrok-annotation1">
+ <p>
+ Grok has the privilege to be supported by a very vivid
+ community, which is contributing also documentation and
+ help. To get you started with Grok, it is highly
+ recommended first to do the <a class="emph"
+ href="http://grok.zope.org/tutorial.html">Grok
+ Tutorial</a>. Afterwards you might find the special <a
+ class="emph"
+ href="http://grok.zope.org/minitutorials/index.html">Grok
+ HOWTOs</a> of value for your work.
+ </p>
+ <p>
+ If you need some more personal advice or want to get
+ involved into Grok core development, have a look at the <a
+ class="emph"
+ href="http://mail.zope.org/mailman/listinfo/grok-dev">Grok-dev
+ Mailinglist</a>. The Grok's headquarter is <a class="emph"
+ href="http://grok.zope.org/">Grok's Home Page</a>.
+ </p>
+ GROK SAY: Have fun!
+ </div>
</div>
<div tal:condition="view/getPathParts">
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/inspect.pt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/inspect.pt 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/inspect.pt 2007-08-06 15:00:38 UTC (rev 78628)
@@ -1,23 +1,216 @@
<html metal:use-macro="context/@@macros/gaia-page">
- <head>
- <title></title>
- </head>
- <body>
- <div metal:fill-slot="content">
- <h1>
- '<span tal:content="view/getId"
- tal:on-error="default">Unnamed Object</span>'
- (object of type
- <span tal:repeat="urlpart python: view.getPathParts(view.getDottedPath())">
+ <div metal:fill-slot="content">
+ <h1>
+ <span tal:content="view/info/name" />
+ <span tal:content="structure view/info/obj_link" />
+ </h1>
+
+ <div class="docgrok-entry">
+ <div class="emph"
+ tal:content="structure view/info/doc" />
+ </div>
+
+ <div>
+ <form method="post" action="">
+ <p>
+ <input type="checkbox" name="show_private"
+ id="show_private"
+ tal:attributes="checked view/show_private"/>
+ <label for="show_private">Show private attributes</label>
+ <input type="submit" value="update" />
+ </p>
+ </form>
+ </div>
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Parent:
+ </h2>
+ <div class="docgrok-annotation1">
+ <div tal:define="parent view/info/parent/obj"
+ tal:condition="parent"
+ i18n:translate="">
<a href=""
- tal:attributes="href urlpart/url"
- tal:content="urlpart/name">dotted_path_part here...</a>
- </span>)
- </h1>
- <h2>Parent</h2>
- <div tal:content="view/getParent">
- My Parent
+ i18n:name="parent"
+ tal:attributes="href view/info/parent/obj_link">
+ <span tal:replace="parent/zope:name"
+ tal:condition="parent/zope:name" />
+ <span tal:condition="not: parent/zope:name"
+ i18n:translate=""><unnamed object></span></a>
+ <span tal:content="structure view/info/parent/class_link" />
+ </div>
+
</div>
+
+ <div class="docgrok-entry"
+ tal:condition="not: view/info/parent/obj">
+ <div class="docgrok-annotation1">
+ No parent object
+ </div>
+ </div>
</div>
- </body>
-</html>
\ No newline at end of file
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Base classes:
+ </h2>
+ <div class="docgrok-annotation1"
+ tal:repeat="klass view/info/bases">
+ <span tal:content="structure klass">Class</span>
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Interfaces provided:
+ </h2>
+ <div class="docgrok-annotation1"
+ tal:repeat="iface view/info/interfaces">
+ <span tal:content="structure iface">Interface</span>
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Attributes and Properties:
+ </h2>
+ <h3 class="docgrok-description">
+ Attributes
+ </h3>
+ <div class="docgrok-annotation1"
+ tal:repeat="attr view/info/attributes">
+ <span class="emph">
+ <span tal:content="attr/name">Name</span>
+ </span>
+
+ <div class="docgrok-annotation1">
+ <div>
+ type: <span tal:content="structure python: attr['type']">
+ Type</span>
+ </div>
+ <div>
+ value:
+ <span tal:condition="attr/value_linkable">
+ <a href=""
+ tal:attributes="href attr/docgrok_link"
+ tal:content="attr/value">Value</a>
+ </span>
+ <span tal:condition="not: attr/value_linkable"
+ tal:content="attr/value">Value</span>
+ </div>
+ <div tal:condition="attr/interface">
+ interface: <span tal:content="structure attr/interface">
+ Interface
+ </span>
+ </div>
+ </div>
+ </div>
+
+
+ <h3 class="docgrok-description">
+ Mappings
+ </h3>
+ <div class="docgrok-annotation1"
+ tal:repeat="item view/info/mappingitems">
+ <span class="emph"
+ tal:content="item/key">
+ mapping-key
+ </span>
+ <div class="docgrok-annotation1">
+ <div>
+ type: <span tal:content="structure python: item['value_type']">
+ Type</span>
+ </div>
+ <div>
+ value:
+ <span tal:condition="item/value_linkable">
+ <a href=""
+ tal:attributes="href item/docgrok_link"
+ tal:content="item/value">Value</a>
+ </span>
+ <span tal:condition="not:item/value_linkable"
+ tal:content="item/value" />
+ </div>
+ </div>
+ </div>
+
+ <h3 class="docgrok-description">
+ Sequences
+ </h3>
+ <div class="docgrok-annotation1"
+ tal:repeat="item view/info/sequenceitems">
+ <span class="emph"
+ tal:content="item/index">
+ sequence-index
+ </span>
+ <div class="docgrok-annotation1">
+ <div>
+ type: <span tal:content="structure python: item['value_type']">
+ Type</span>
+ </div>
+ <div>
+ value:
+ <span tal:condition="item/value_linkable">
+ <a href=""
+ tal:attributes="href item/docgrok_link"
+ tal:content="item/value">Value</a>
+ </span>
+ <span tal:condition="not:item/value_linkable"
+ tal:content="item/value" />
+ </div>
+ </div>
+ </div>
+
+ <h3 class="docgrok-description">
+ Annotations
+ </h3>
+ <div class="docgrok-annotation1"
+ tal:repeat="item view/info/annotations">
+ <span class="emph"
+ tal:content="item/key">
+ annotation-key
+ </span>
+ <div class="docgrok-annotation1">
+ <div>
+ type: <span tal:content="structure item/value_type">
+ Type</span>
+ </div>
+ <div>
+ value:
+ <span tal:condition="item/value_linkable">
+ <a href=""
+ tal:attributes="href item/docgrok_link"
+ tal:content="item/value">value</a>
+ </span>
+ <span tal:condition="not: item/value_linkable"
+ tal:content="item/value" />
+ </div>
+ </div>
+ <p></p>
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Methods:
+ </h2>
+ <div class="docgrok-annotation1"
+ tal:repeat="method view/info/methods">
+ <span tal:content="method/name"
+ class="emph"
+ >method</span><span tal:content="method/signature"
+ class="emph"
+ >method</span>
+ <div tal:condition="method/doc"
+ tal:content="structure method/doc">Method documentation</div>
+ <div class="docgrok-annotation1"
+ tal:condition="method/interface">
+ interface: <span tal:content="structure method/interface">interface</span>
+ </div>
+ <p></p>
+ </div>
+ </div>
+
+ </div>
+ <div metal:fill-slot="footer">asda</div>
+</html>
Added: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/logout.pt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/logout.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/logout.pt 2007-08-06 15:00:38 UTC (rev 78628)
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <title>Logged out</title>
+</head>
+<body>
+ You have been logged out.
+</body>
+</html>
\ No newline at end of file
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt 2007-08-06 15:00:38 UTC (rev 78628)
@@ -1,5 +1,6 @@
<html xmlns="http://www.w3.org/1999/xhtml"
- metal:define-macro="gaia-page">
+ i18n:domain="zope"
+ metal:define-macro="gaia-page">
<head>
<title
metal:define-slot="title"
@@ -19,6 +20,18 @@
tal:attributes="src view/static/grok-admin.jpg" />
</a>
</div>
+
+ <div id="logout" metal:define-macro="logged_user">
+ <span tal:condition="view/is_authenticated">
+ <span i18n:translate="">User:
+ <span tal:replace="request/principal/title"
+ i18n:name="user_title">User</span>
+ </span>
+ [<a href=""
+ tal:attributes="href string:${view/root_url}/logout">log out</a>]
+ </span>
+ </div>
+
<div id="breadcrumbs">
<div id="banner-shadow">
Modified: Sandbox/ulif/grok-adminui/src/grok/ftests/admin/apps.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/ftests/admin/apps.py 2007-08-06 13:09:23 UTC (rev 78627)
+++ Sandbox/ulif/grok-adminui/src/grok/ftests/admin/apps.py 2007-08-06 15:00:38 UTC (rev 78628)
@@ -3,58 +3,9 @@
>>> import grok
>>> grok.grok('grok.ftests.admin.apps')
-First setup the pluggable authentication system for session based
-authentication. This is normaly invoked by an event
-handler. Unfortunately the event handler seems not to be called, if
-the ftesting setup is set up. We therefore set up the PAU manually.
-
- >>> root = getRootFolder()
- >>> root is not None
- True
-
- >>> import grok.admin
- >>> principal_credentials = grok.admin.getPrincipalCredentialsFromZCML()
- >>> principal_credentials
- [{u'login': u'mgr', u'password': u'mgrpw', u'id': u'zope.mgr', u'title': u'Manager'}]
-
- >>> grok.admin.setupSessionAuthentication(root_folder = root, principal_credentials = principal_credentials)
-
-We should get a login page if trying to get something unauthenticated.
-
>>> from zope.testbrowser.testing import Browser
>>> browser = Browser()
- >>> browser.handleErrors = True
- >>> browser.open("http://localhost/")
-
- >>> print browser.contents
- <html xmlns="http://www.w3.org/1999/xhtml">
- ... <title>Grok Login</title>
- ...
-
-Now try to log in using *wrong* credentials
-
- >>> browser.getControl(name='login').value = 'dumbtry'
- >>> browser.getControl('Login').click()
- >>> print browser.contents
- <html xmlns="http://www.w3.org/1999/xhtml">
- ... <title>Grok Login</title>
- ...
-
-Okay, we got the login screen again. What about the correct credentials?
-
- >>> browser.getControl(name='login').value = 'mgr'
- >>> browser.getControl(name='password').value = 'mgrpw'
- >>> browser.getControl('Login').click()
- >>> print browser.contents
- <html xmlns="http://www.w3.org/1999/xhtml">
- ... <title>grok administration interface</title>
- ...
-
-Fine. Now we are authorized and can do, whatever we want. To stay
-authenticated, we set a header here.
-
>>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
-
We fetch the standard page, which should provide us a menu to get all
installable grok applications/components.
@@ -66,6 +17,15 @@
... <legend>Add application</legend>
...
+The opening screen should inform us, that there are no applications
+installed yet:
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <p ...>Currently no applications are installed.</p>
+ ...
+
We are able to add a mammoth manager...
>>> browser.getControl('Name your new app:',index=13).value = 'my-mammoth-manager'
@@ -93,9 +53,19 @@
>>> print browser.url
http://localhost/my-mammoth-manager
+We can go to the object browser for every installed application:
+
+ >>> browser.open("http://localhost/applications")
+ >>> browser.getLink('object browser').click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ...<span ...>...<a href=...>MammothManager</a> object at ...></span>
+ ...
+
We are able to delete installed mammoth-managers
- >>> browser.open("http://localhost/")
+ >>> browser.open("http://localhost/applications")
>>> print browser.contents
<html xmlns="http://www.w3.org/1999/xhtml">
...
@@ -107,6 +77,8 @@
>>> print browser.contents
<html xmlns="http://www.w3.org/1999/xhtml">
...
+ ... <p ...>Currently no applications are installed.</p>
+ ...
...<legend>Add application</legend>
...
Added: Sandbox/ulif/grok-adminui/src/grok/ftests/admin/loginlogout.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/ftests/admin/loginlogout.py (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/ftests/admin/loginlogout.py 2007-08-06 15:00:38 UTC (rev 78628)
@@ -0,0 +1,92 @@
+"""
+
+ >>> import grok
+ >>> grok.grok('grok.ftests.admin.loginlogout')
+
+First setup the pluggable authentication system for session based
+authentication. This is normaly invoked by an event
+handler. Unfortunately the event handler seems not to be called, if
+the ftesting setup is set up. We therefore set up the PAU manually.
+
+ >>> root = getRootFolder()
+ >>> root is not None
+ True
+
+ >>> import grok.admin
+ >>> principal_credentials = grok.admin.getPrincipalCredentialsFromZCML()
+ >>> principal_credentials
+ [{u'login': u'mgr', u'password': u'mgrpw', u'id': u'zope.mgr', u'title': u'Manager'}]
+
+ >>> grok.admin.setupSessionAuthentication(root_folder = root, principal_credentials = principal_credentials)
+
+We should get a login page if trying to get something unauthenticated.
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.handleErrors = True
+ >>> browser.open("http://localhost/")
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <title>Grok Login</title>
+ ...
+
+Now try to log in using *wrong* credentials
+
+ >>> browser.getControl(name='login').value = 'dumbtry'
+ >>> browser.getControl('Login').click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <title>Grok Login</title>
+ ...
+
+Okay, we got the login screen again. What about the correct credentials?
+
+ >>> browser.getControl(name='login').value = 'mgr'
+ >>> browser.getControl(name='password').value = 'mgrpw'
+ >>> browser.getControl('Login').click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <title>grok administration interface</title>
+ ...
+
+The new screen should contain a link for logging out:
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <span>User:
+ ...Manager
+ ...[<a href="http://localhost/logout">log out</a>]
+ ...
+
+Fine. Now we are authorized and can do, whatever we want. Let's log out:
+
+ >>> outlink = browser.getLink('log out')
+ >>> outlink
+ <Link text='log out' url='http://localhost/logout'>
+
+ >>> outlink.click()
+ >>> print browser.contents
+ <html>
+ ... You have been logged out.
+ ...
+
+Looks okay. But are we really logged out? Let's try to fetch a page:
+
+ >>> browser.open("http://localhost/")
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <title>Grok Login</title>
+ ...
+ ... <td><input id="login" type="text" name="login" /></td>
+ ...
+
+Yes, we are.
+
+ ...
+ ... <legend>Add application</legend>
+ ...
+
+
+"""
+
Added: Sandbox/ulif/grok-adminui/src/grok/ftests/admin/objectbrowser.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/ftests/admin/objectbrowser.py (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/ftests/admin/objectbrowser.py 2007-08-06 15:00:38 UTC (rev 78628)
@@ -0,0 +1,137 @@
+"""
+
+ >>> import grok
+ >>> grok.grok('grok.ftests.admin.objectbrowser')
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+We fetch the documentation page, which should give us a tiny overview
+over documentation:
+
+ >>> browser.open("http://localhost/docgrok")
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... Welcome to DocGrok...
+ ...
+
+On the documentation page there should be a link to the ZODB root
+folder:
+
+ >>> root_link = browser.getLink('ZODB root folder')
+ >>> root_link
+ <Link text='ZODB root folder' url='http://localhost/@@inspect.html'>
+
+The root folder got no name:
+
+ >>> root_link.click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <span><unnamed object></span>
+ ...
+
+and is of type Folder.
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <span ...>...<a ...>Folder</a> object at ...></span>
+ ...
+
+It's class documentation should be linked in the head of page:
+
+ >>> browser.getLink('Folder').url
+ 'http://localhost/docgrok/zope/app/folder/folder/Folder/'
+
+We also get the docstring of the root folder:
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ...<p>The standard Zope Folder implementation.</p>
+ ...
+
+A checkbox gives us control over private members and attributes of the
+object displayed:
+
+ >>> checkbox = browser.getControl('Show private attributes')
+ >>> checkbox
+ <ItemControl name='show_private' type='checkbox' optionValue='on' selected=False>
+
+By default the checkbox is not selected. Therefore we check for an
+arbitrary private method to be displayed or not. For example the
+``__dict__`` method. By default no __dict__ method will be displayed:
+
+ >>> '__dict__' in browser.contents
+ False
+
+Now let's tick the checkbox and update the view:
+
+ >>> checkbox.selected = True
+ >>> checkbox.selected
+ True
+
+ >>> browser.getControl('update').click()
+
+Now the private method should be displayed:
+
+ >>> '__dict__' in browser.contents
+ True
+
+Here we go :-)
+
+Okay, now let's examine the displayed data a bit. We are currently the
+object browser's view for the root folder. The root folder got no
+parent, which should be displayed:
+
+ >>> 'No parent object' in browser.contents
+ True
+
+One of the base classes of the root folder is the class
+``persistent.Persistent``. We not only want that displayed but also a
+link to the class documentation of that class:
+
+ >>> link = browser.getLink("Persistent'")
+ >>> link.url
+ 'http://localhost/docgrok/persistent/Persistent'
+
+The same for interfaces. The root folder implements
+``persistent.interfaces.IPersistent``:w
+
+ >>> link = browser.getLink('IPersistent')
+ >>> link.url
+ 'http://localhost/docgrok/persistent/interfaces/IPersistent/'
+
+Now the attributes and properties. The root folder got an attribute
+``data``:
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ...<h3 ...>
+ ...Attributes
+ ...</h3>
+ ... <span>data</span>
+ ... <div>
+ ... value:
+ ... <a href="http://localhost/docgrok-obj/data/@@inspect.html"><BTrees.OOBTree.OOBTree object at ...></a>
+ ... </div>
+ ...
+
+
+
+"""
+
+import grok
+
+class MammothManager(grok.Application, grok.Container):
+ """A mammoth manager"""
+ pass
+
+class Index(grok.View):#
+
+ def render(self):
+ return u"Let's manage some mammoths!"
More information about the Checkins
mailing list