[Plone-Users] Error when viewing news items in Plone 4.0

68 views
Skip to first unread message

bokjeld

unread,
Jun 26, 2011, 5:01:47 PM6/26/11
to plone...@lists.sourceforge.net
I am using Plone 4.0 installed with unified installer. When I try to view any
of my news items, the browser gives the following error message:

TypeError("unsupported operand type(s) for +: 'NoneType' and 'int'",) (Also,
the following error occurred while attempting to render the standard error
message, please see the event log for full details: unsupported operand
type(s) for +: 'NoneType' and 'int')

This might be a result of me having installed collective.sharerizer 1.1 (via
buildout). I have taken this add-on out of the active add-ons, but that does
not help. So if anyone could help me out on this, I would be grateful :-)

Børge

The full traceback is attached here:

Traceback (innermost last):
Module ZPublisher.Publish, line 127, in publish
Module ZPublisher.mapply, line 77, in mapply
Module ZPublisher.Publish, line 47, in call_object
Module Shared.DC.Scripts.Bindings, line 324, in __call__
Module Products.PloneHotfix20110531, line 106, in _patched_bindAndExec
Module Shared.DC.Scripts.Bindings, line 361, in _bindAndExec
Module Products.CMFCore.FSPageTemplate, line 240, in _exec
Module Products.CMFCore.FSPageTemplate, line 180, in pt_render
Module Products.PageTemplates.PageTemplate, line 80, in pt_render
Module zope.pagetemplate.pagetemplate, line 115, in pt_render
Module zope.tal.talinterpreter, line 271, in __call__
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 888, in do_useMacro
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 533, in do_optTag_tal
Module zope.tal.talinterpreter, line 518, in do_optTag
Module zope.tal.talinterpreter, line 513, in no_tag
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 852, in do_condition
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 533, in do_optTag_tal
Module zope.tal.talinterpreter, line 518, in do_optTag
Module zope.tal.talinterpreter, line 513, in no_tag
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 531, in do_optTag_tal
Module zope.tal.talinterpreter, line 513, in no_tag
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 742, in do_insertStructure_tal
Module Products.PageTemplates.Expressions, line 220, in evaluateStructure
Module zope.tales.tales, line 696, in evaluate
- URL:
file:/usr/local/plone4/buildout-cache/eggs/plonetheme.sunburst-1.0.2-py2.6.egg/plonetheme/sunburst/skins/sunburst_templates/main_template.pt
- Line 35, Column 8
- Expression: <StringExpr u'plone.htmlhead.links'>
- Names:
{'container': <PloneSite at /kjeldstad>,
'context': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'default': ,
'here': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'loop': {},
'nothing': None,
'options': {'args': ()},
'repeat': <Products.PageTemplates.Expressions.SafeMapping object at
0x7f68150f02b8>,
'request': &lt;HTTPRequest,
URL=https://test.kjeldstad.com/kjeldstad/news/support-for-ai-weiwei/newsitem_view&gt;,
'root': <Application at >,
'template': <FSPageTemplate at /kjeldstad/newsitem_view used for
/kjeldstad/news/support-for-ai-weiwei>,
'traverse_subpath': [],
'user': <PropertiedUser 'admin'>}
Module zope.contentprovider.tales, line 80, in __call__
Module plone.app.viewletmanager.manager, line 85, in render
Module plone.app.layout.viewlets.common, line 48, in render
Module Products.Five.browser.pagetemplatefile, line 126, in __call__
Module Products.Five.browser.pagetemplatefile, line 60, in __call__
Module zope.pagetemplate.pagetemplate, line 115, in pt_render
Module zope.tal.talinterpreter, line 271, in __call__
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 852, in do_condition
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 531, in do_optTag_tal
Module zope.tal.talinterpreter, line 513, in no_tag
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 583, in do_setLocal_tal
Module zope.tales.tales, line 696, in evaluate
- URL:
/usr/local/plone4/buildout-cache/eggs/plone.app.layout-2.0.2-py2.6.egg/plone/app/layout/nextprevious/links.pt
- Line 17, Column 4
- Expression: <PathExpr standard:u'view/next'>
- Names:
{'args': (),
'container': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'context': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'default': ,
'here': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'loop': {},
'nothing': None,
'options': {},
'repeat': <Products.PageTemplates.Expressions.SafeMapping object at
0x7f6818ae0730>,
'request': &lt;HTTPRequest,
URL=https://test.kjeldstad.com/kjeldstad/news/support-for-ai-weiwei/newsitem_view&gt;,
'root': <Application at >,
'template':
<Products.Five.browser.pagetemplatefile.ViewPageTemplateFile object at
0x5a56b50>,
'traverse_subpath': [],
'user': <PropertiedUser 'admin'>,
'view': <Products.Five.viewlet.metaconfigure.NextPreviousLinksViewlet
object at 0x7f68150da150>,
'views': <Products.Five.browser.pagetemplatefile.ViewMapper object at
0x7f68196f5f50>}
Module zope.tales.expressions, line 217, in __call__
Module Products.PageTemplates.Expressions, line 157, in _eval
Module Products.PageTemplates.Expressions, line 119, in render
Module plone.app.layout.nextprevious.view, line 19, in next
Module plone.app.folder.nextprevious, line 30, in getNextItem
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'


--
View this message in context: http://plone.293351.n2.nabble.com/Error-when-viewing-news-items-in-Plone-4-0-tp6518151p6518151.html
Sent from the General Questions mailing list archive at Nabble.com.

------------------------------------------------------------------------------
All of the data generated in your IT infrastructure is seriously valuable.
Why? It contains a definitive record of application performance, security
threats, fraudulent activity, and more. Splunk takes this data and makes
sense of it. IT sense. And common sense.
http://p.sf.net/sfu/splunk-d2d-c2
_______________________________________________
Plone-Users mailing list
Plone...@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/plone-users

ajung

unread,
Jun 27, 2011, 12:40:44 AM6/27/11
to plone...@lists.sourceforge.net
You reported that already some days ago - no need to re-post questions within
some days.
If you have are having collective.shareizer in mind then contact the
maintainer or file a bug report.

-aj


--
View this message in context: http://plone.293351.n2.nabble.com/Error-when-viewing-news-items-in-Plone-4-0-tp6518151p6518972.html

Dieter Maurer

unread,
Jun 27, 2011, 5:10:52 AM6/27/11
to bokjeld, plone...@lists.sourceforge.net
bokjeld wrote at 2011-6-26 14:01 -0700:
>I am using Plone 4.0 installed with unified installer. When I try to view any
>of my news items, the browser gives the following error message:
>
>TypeError("unsupported operand type(s) for +: 'NoneType' and 'int'",) (Also,
>the following error occurred while attempting to render the standard error
>message, please see the event log for full details: unsupported operand
>type(s) for +: 'NoneType' and 'int')
>
>This might be a result of me having installed collective.sharerizer 1.1 (via
>buildout). I have taken this add-on out of the active add-ons, but that does
>not help. So if anyone could help me out on this, I would be grateful :-)
>
>Børge
>
>The full traceback is attached here:
>
>Traceback (innermost last):
> ...

Due to its traceback, this message is much more helpful
for an analysis than your previous post.


You will find some hints near line 30 of
"plone.app.folder.nextprevious". You should see there a "+" operation
which tries to combine "None" and an integer.
It is highly likely, that the "None" causes the problem.
Try to find out where this "None" comes from. This will lead you towards
the primary problem cause.


Some form of debugging might sometime during the process to be helpfull.
There is a HowTo how to debug Plone (there are also many threads about
debugging in this list's archive, easily searchable via "gmane").

--
Dieter

Børge Kjeldstad

unread,
Jun 27, 2011, 3:26:49 PM6/27/11
to Dieter Maurer, plone...@lists.sourceforge.net
Thank you Dieter and Espen for valuable answers. I have found a
solution, but it is odd: unticking the "Enable next previous navigation"
under settings for the News-folder...

First I tried to copy one of my error-making news items to another Plone
site on the same instance. There it worked and showed up fine.

Then I went into the file:

/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py

It's content up to line 35 is:

from zope.interface import implements
from zope.component import adapts
from Products.CMFCore.utils import getToolByName
from plone.app.layout.nextprevious.interfaces import INextPreviousProvider
from plone.app.folder.folder import IATUnifiedFolder


class NextPrevious(object):
""" adapter for acting as a next/previous provider """
implements(INextPreviousProvider)
adapts(IATUnifiedFolder)

def __init__(self, context):
self.context = context
props = getToolByName(context, 'portal_properties').site_properties
self.vat = props.getProperty('typesUseViewActionInListings', ())

@property
def enabled(self):
return self.context.getNextPreviousEnabled()

def getNextItem(self, obj):
""" return info about the next item in the container """
pos = self.context.getObjectPosition(obj.getId())
ordering = self.context.getOrdering()
try:
try: # first try `__getitem__`
next = ordering[pos + 1]
except TypeError:
next = ordering.idsInOrder()[pos + 1]
return self.getData(self.context[next])
except IndexError: # in case next > len(folder)
return None

def getPreviousItem(self, obj):


............ etc...


Now I remember that I did something with my news item/section, after I
had installed collective.sharerizer, to see if I could ad some
sharebutton on it. I think it was in the "Manage portlets". I had no
success and probably aborted the operation. In my "undo" list I see the
following transactions done at the most plausible error time:

/kjeldstad/news/plone_lock_operations/safe_unlock by admin

I tried to undo this transaction, but it had no effect.

Then I unticked the "Enable next previous navigation" under settings for
the News-folder; and Bob's your uncle!!!!


The problem disappeared... When I re-tick the "Enable next previous
navigation", the problem comes back again. The problem also shows up if
I enable next previous navigation on the news folder on my other plone site.

So this might not have anything to do with collective.sharerizer. But,
should not I be able to enable next previous navigation on my news items?

If I am adviced to do so, I'll find out how I can file a bug on this
issue, and file it.

Thank you
Børge

Dieter Maurer

unread,
Jun 28, 2011, 1:50:06 AM6/28/11
to Børge Kjeldstad, plone...@lists.sourceforge.net
Børge Kjeldstad wrote at 2011-6-27 21:26 +0200:
> ...

>Then I went into the file:
>
>/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py
>
>It's content up to line 35 is:
>
> ...

>class NextPrevious(object):
> """ adapter for acting as a next/previous provider """
> ...

> def getNextItem(self, obj):
> """ return info about the next item in the container """
> pos = self.context.getObjectPosition(obj.getId())
> ordering = self.context.getOrdering()
> try:
> try: # first try `__getitem__`
> next = ordering[pos + 1]
> except TypeError:
> next = ordering.idsInOrder()[pos + 1]

I expect that the previous line is the one with the "TypeError".

In this case, "pos" would be "None" and the problem would come from
"getObjectPosition" -- an indication, that the problem is not
with the news object itself but with its container or the catalog.

In your place, I would check how "getObjectPosition" is implemented
and why it returns "None" rather than an integer.
Once, you have found out, we may see how to fix it.

Alternatively, you could reindex your catalog and check whether this
gets rid of your problem. Chances are not that high, but reindexing
is much faster than analysing "getObjectPosition".

Børge Kjeldstad

unread,
Jun 28, 2011, 6:18:32 PM6/28/11
to Dieter Maurer, plone...@lists.sourceforge.net
Thank you Dieter,

I believe I reindexed one of my Plone-sites' portal_catalog by pressing
the "Update Catalog" button. That did not help.

To investigate the "getObjectPosition" issue, I went one step back in my
traceback, to line 19 in the file
/usr/local/plone4/buildout-cache/eggs/plone.app.layout-2.0.2-py2.6.egg/plone/app/layout/nextprevious/view.py.
The file looks like this:

from zope.component import getMultiAdapter

from plone.app.layout.viewlets import ViewletBase
from plone.app.layout.nextprevious.interfaces import INextPreviousProvider

from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
from Acquisition import aq_inner, aq_parent


class NextPreviousView(BrowserView):
"""Information about next/previous navigation
"""

def next(self):
provider = self._provider()
if provider is None:
return None
return provider.getNextItem(aq_inner(self.context))

def previous(self):
provider = self._provider()
if provider is None:
return None
return provider.getPreviousItem(aq_inner(self.context))

def enabled(self):
provider = self._provider()
if provider is None:
return False
return provider.enabled

def _provider(self):
# Note - the next/previous provider is the container of this
object!
# This may not support next/previous navigation, so code
defensively
return INextPreviousProvider(aq_parent(aq_inner(self.context)),
None)

def isViewTemplate(self):
plone = getMultiAdapter((self.context, self.request),
name=u'plone_context_state')
return plone.is_view_template()


class NextPreviousViewlet(ViewletBase, NextPreviousView):
index = ZopeTwoPageTemplateFile('nextprevious.pt')


class NextPreviousLinksViewlet(ViewletBase, NextPreviousView):
index = ZopeTwoPageTemplateFile('links.pt')


....... End of file.....


This is far beyond my python knowledge, but it looks like None will be
returned here if "provider is None"

And that thing "INextPreviousProvider" is found in the file:

/usr/local/plone4/buildout-cache/eggs/plone.app.layout-2.0.2-py2.6.egg/plone/app/layout/nextprevious/interfaces.py.
This file's content looks like this:

from zope.interface import Interface
from zope.schema import Bool

class INextPreviousProvider(Interface):
"""A folderish component capable of describing the next and previous
item relative to a particular id.
"""

enabled = Bool(title=u"True if next/previous behaviour is enabled")

def getNextItem(obj):
"""Returns information about next item in the container relative to
the given object.

This is a dict with the following keys:

- id, the id of the object
- url, the url of the object
- title, the title of the object
- description, a description of the object
- portal_type, the object's portal type
"""

def getPreviousItem(obj):
"""Returns the previous item in the container relative to the given
object
"""

.....End of file.....

I am not quite sure where I should continue from now......

Thank you
Børge

Dieter Maurer

unread,
Jun 29, 2011, 1:51:05 AM6/29/11
to Børge Kjeldstad, plone...@lists.sourceforge.net
Børge Kjeldstad wrote at 2011-6-29 00:18 +0200:
>I believe I reindexed one of my Plone-sites' portal_catalog by pressing
>the "Update Catalog" button. That did not help.

Hope was small only.

>To investigate the "getObjectPosition" issue, I went one step back in my
>traceback, to line 19 in the file
>/usr/local/plone4/buildout-cache/eggs/plone.app.layout-2.0.2-py2.6.egg/plone/app/layout/nextprevious/view.py.
>The file looks like this:
>
>from zope.component import getMultiAdapter
>
>from plone.app.layout.viewlets import ViewletBase
>from plone.app.layout.nextprevious.interfaces import INextPreviousProvider
>
>from Products.Five.browser import BrowserView
>from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
>from Acquisition import aq_inner, aq_parent
>
>
>class NextPreviousView(BrowserView):
> """Information about next/previous navigation
> """
>
> def next(self):
> provider = self._provider()
> if provider is None:
> return None
> return provider.getNextItem(aq_inner(self.context))

You are not yet on the correct trace.

Your original traceback tells you that the provider has been found
(looking for it does not yield "None"), its "getNextItem" is called
and it is this "getNextIdem" call which results in the
"TypeError: None + Int".

This means (quite surely) that "self.context.getObjectPosition(obj.getId())"
near "Module plone.app.folder.nextprevious, line 20+, in getNextItem"
returns "None". You have to find out why.

I can see two potential causes:

* "self.context.getObjectPosition" is to blame.

Almost surely, "self.context" is the folder containing your object.

* "obj" (it is provided by "NextPriviousView.next") is wrong.


The easiest way to find out it by debugging -- e.g. by
means of "Products.PDBDebugMode" (or something like this),
by calling the involved methods in an interactive Python session
or by putting a breakpoint ("import pdb; pdb.set_trace()")
into the "except TypeError" block of
"plone.app.folder.nextprevious(getNextItem)".

An alternative (so less sure) would be code inspection of
"getObjectPosition" (likely a "Folder" method).


Should you start to embrace error analysis in an interactive
Python session (as I do), I can provide a small function ("definedBy")
which determines where a method definition comes from.
This can be helpful (e.g.) to determine where "getObjectPosition"
is defined. For isolated cases, a textual search over the source tree
might be sufficient, however.

Børge Kjeldstad

unread,
Jul 4, 2011, 5:21:07 PM7/4/11
to Dieter Maurer, plone...@lists.sourceforge.net
Thank you again Dieter for your reply and i apology for my late reply.
Starting my first Plone debugging was a huge step...

Anyway: I did your:

> putting a breakpoint ("import pdb; pdb.set_trace()")
> into the "except TypeError" block of
> "plone.app.folder.nextprevious(getNextItem)"

It gave the following results:

2011-07-04 23:01:01 INFO Zope Ready to handle requests
>
/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(31)getNextItem()
-> next = ordering.idsInOrder()[pos + 1]
(Pdb) l
26 try:
27 try: # first try `__getitem__`
28 next = ordering[pos + 1]
29 except TypeError:
30 import pdb; pdb.set_trace()
31 -> next = ordering.idsInOrder()[pos + 1]
32 return self.getData(self.context[next])
33 except IndexError: # in case next > len(folder)
34 return None
35
36 def getPreviousItem(self, obj):
(Pdb) dir()
['obj', 'ordering', 'pdb', 'pos', 'self']
(Pdb) method
*** NameError: name 'method' is not defined
(Pdb) obj
<ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>
(Pdb) ordering
<plone.folder.unordered.UnorderedOrdering object at 0x7a3ad50>
(Pdb) pos
(Pdb) self
<plone.app.folder.nextprevious.NextPrevious object at 0x7a3ac90>

....end of snip...

I then opened the file
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py

It's content is :

from Acquisition import aq_base


from zope.interface import implements
from zope.component import adapts

from plone.folder.interfaces import IOrdering, IOrderableFolder


class UnorderedOrdering(object):
""" This implementation provides no ordering. """
implements(IOrdering)
adapts(IOrderableFolder)

def __init__(self, context):
self.context = context

def notifyAdded(self, id):
pass

def notifyRemoved(self, id):
pass

def idsInOrder(self):
return aq_base(self.context).objectIds(ordered=False)

def getObjectPosition(self, id):
return None


...end of file...


There I see:

getObjectPosition(self, id):
return None


Can this be the error? Maybe I should comment out the "return None" and
see what happens?


By the way: I also googled your function "definedBy". Is it this one
(and how is it used)?:

def definedBy(name, class_):
'''return *class_* base class defining *name*.

*class_* may (now) also be an object. In this case, its class is used.
'''
class_ = aq_base(class_)
if not hasattr(class_, '__bases__'): class_ = class_.__class__
for cl in getmro(class_):
if hasattr(cl,'__dict__'):
if cl.__dict__.has_key(name): return cl
elif hasattr(cl, name): return cl
return None

...end of snip...


Thank you again
Børge

Dieter Maurer

unread,
Jul 5, 2011, 1:58:16 AM7/5/11
to Børge Kjeldstad, plone...@lists.sourceforge.net
Børge Kjeldstad wrote at 2011-7-4 23:21 +0200:
> ...
>Anyway: I did your:
>
> > putting a breakpoint ("import pdb; pdb.set_trace()")
> > into the "except TypeError" block of
> > "plone.app.folder.nextprevious(getNextItem)"
>
>It gave the following results:
>
>2011-07-04 23:01:01 INFO Zope Ready to handle requests
> >
>/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(31)getNextItem()
>-> next = ordering.idsInOrder()[pos + 1]
>(Pdb) l
> 26 try:
> 27 try: # first try `__getitem__`
> 28 next = ordering[pos + 1]
> 29 except TypeError:
> 30 import pdb; pdb.set_trace()
> 31 -> next = ordering.idsInOrder()[pos + 1]
> 32 return self.getData(self.context[next])
> 33 except IndexError: # in case next > len(folder)
> 34 return None
> 35
> 36 def getPreviousItem(self, obj):
>(Pdb) dir()
>['obj', 'ordering', 'pdb', 'pos', 'self']
> ...
>(Pdb) pos

This proves the hypothesis that "pos" (wrongly) is "None".


To find out why, you can use one of the PDB "debug" or "j[ump]" commands.

"debug" allows you to interactively examine the evaluation of a command.
In your case, this would be the command that has produced the wrong
value for "pos".

"jump" allows you to transfer (execution) control to a different line (with
some restrictions). You may be able to use it to reexecute the
wrong "pos = ..." assignment under your control and examine it.


After both "debug" and "jump", you would use "s[tep]" (step into function
call) "n[ext]" (step over function call), "r[eturn]" (leave function call)
to control the execution and "p[rint]", "pp" (pretty print),
"args" to examine the state.

>(Pdb) self
><plone.app.folder.nextprevious.NextPrevious object at 0x7a3ac90>
>
>....end of snip...
>
>I then opened the file
>/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py

>It's content is :
>
>from Acquisition import aq_base
>from zope.interface import implements
>from zope.component import adapts
>from plone.folder.interfaces import IOrdering, IOrderableFolder
>
>
>class UnorderedOrdering(object):
> """ This implementation provides no ordering. """
> implements(IOrdering)
> adapts(IOrderableFolder)
>

> ...
>There I see:
>
>getObjectPosition(self, id):
> return None

This may explain the wrong "None" which is causing the difficulty
you observe.

For me, this looks like a bug in "plone.app.folder".
When Plone expects that some folders do not support the next/previous
functionality (because they are unordered), this functionality
should be automatically disabled for those folders.

You could try to add as a workaround a line:

if pos is None: return None

after the "pos" assignment in "nextprevious".

If this solves your problem, please file a bug report against
"plone.app.folder".

> ...


>Can this be the error? Maybe I should comment out the "return None" and
>see what happens?
>
>
>By the way: I also googled your function "definedBy". Is it this one
>(and how is it used)?:
>
>def definedBy(name, class_):
> '''return *class_* base class defining *name*.
>
> *class_* may (now) also be an object. In this case, its class is used.
> '''

It is. However, it might be an old version (one which does not
yet use the method resolution order in newer Python versions).
Should you see problems, you may tell me and I will look whether
I can provide a newer version.


How is it used?

Well, I am a strong protagonist of well chosen names
and terse but concise documentation. Usually, you can have
confidence in the names I have chosen.

You see here a function with name "definedBy" and arguments
"name" and "class_". Its name suggests that "definedBy(name, cls)"
tells you where "name" is defined in the context of "cls".
The function's docstring tells you that the result is the defining
class (from which you can ask the module, the class is defined in).
The docstring also tells you that you can pass an object rather
than a class and that the object's class is used in this case.

As an example: in order to find out which class
defines "idsInOrder" after you entered your breakpoint above,
you could use "!import DUtils; DUtils.definedBy('idsInOrder', ordering)"
(this assumes, that "DUtils.py" has been placed somewhere where
Python looks for modules, e.g. the current working directory).
If you have the class, its "__module__" attribute tells you the module.

Børge Kjeldstad

unread,
Jul 9, 2011, 3:38:02 PM7/9/11
to Dieter Maurer, plone...@lists.sourceforge.net
Thank you once more Dieter. This was a tough one, but I might have
advanced further:

Den 05. juli 2011 07:58, skrev Dieter Maurer:
> Børge Kjeldstad wrote at 2011-7-4 23:21 +0200:
>> (Pdb) dir()
>> ['obj', 'ordering', 'pdb', 'pos', 'self']
>> ...
>> (Pdb) pos
>
> This proves the hypothesis that "pos" (wrongly) is "None".
>
>
> To find out why, you can use one of the PDB "debug" or "j[ump]" commands.
>

Out of some reason I think the "debug" comand made Plone hang. Or at
least I could hardly do anything more in the terminal window and shut it
down. But "j[ump]" worked, and I jumped back to the pos assignment in
nextprevious and found some reference to ATFolder:

>
/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(31)getNextItem()->None


-> next = ordering.idsInOrder()[pos + 1]
(Pdb) l

19 def enabled(self):
20 return self.context.getNextPreviousEnabled()
21
22 def getNextItem(self, obj):
23 """ return info about the next item in the container """
24 -> pos = self.context.getObjectPosition(obj.getId())
25 ordering = self.context.getOrdering()


26 try:
27 try: # first try `__getitem__`
28 next = ordering[pos + 1]
29 except TypeError:

(Pdb) self.context.getObjectPosition
<bound method ATFolder.getObjectPosition of <ATFolder at /kjeldstad/news>>
(Pdb)

After a bit of googling I opened the file:
/usr/local/plone4/buildout-cache/eggs/Products.Archetypes-1.6.1-py2.6.egg/Products/Archetypes/OrderedBaseFolder.py


It contains a getObjectPosition definition at the bottom of the
following extract:

"""
OrderedBaseFolder derived from OrderedFolder by Stephan Richter, iuveno AG.
OrderedFolder adapted to Zope 2.7 style interface by
Jens....@jensquadrat.de
"""

from zope.interface import implements

from AccessControl import ClassSecurityInfo
from App.class_init import InitializeClass
from DocumentTemplate import sequence
from OFS.interfaces import IOrderedContainer
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.interfaces import IDynamicType
from Products.CMFCore import permissions
from zExceptions import NotFound

from Products.Archetypes.BaseFolder import BaseFolder
from Products.Archetypes.ExtensibleMetadata import ExtensibleMetadata


class OrderedContainer:

implements(IOrderedContainer)

security = ClassSecurityInfo()

security.declareProtected(permissions.ModifyPortalContent,
'moveObject')
def moveObject(self, id, position):
obj_idx = self.getObjectPosition(id)
if obj_idx == position:
return None
elif position < 0:
position = 0

metadata = list(self._objects)
obj_meta = metadata.pop(obj_idx)
metadata.insert(position, obj_meta)
self._objects = tuple(metadata)

# TODO here the implementing of IOrderedContainer starts
# this should be replaced by mixing in the 2.7 specific class
# OSF.OrderedContainer.OrderedContainer

security.declareProtected(permissions.ModifyPortalContent,
'moveObjectsByDelta')
def moveObjectsByDelta(self, ids, delta, subset_ids=None):
""" Move specified sub-objects by delta.
"""
if isinstance(ids, basestring):
ids = (ids,)
min_position = 0
objects = list(self._objects)
if subset_ids == None:
# OLD: subset_ids = [ obj['id'] for obj in objects ]
subset_ids = self.getCMFObjectsSubsetIds(objects)
else:
subset_ids = list(subset_ids)
# unify moving direction
if delta > 0:
ids = list(ids)
ids.reverse()
subset_ids.reverse()
counter = 0

for id in ids:
try:
old_position = subset_ids.index(id)
except ValueError:
continue
new_position = max( old_position - abs(delta), min_position )
if new_position == min_position:
min_position += 1
if not old_position == new_position:
subset_ids.remove(id)
subset_ids.insert(new_position, id)
counter += 1

if counter > 0:
if delta > 0:
subset_ids.reverse()
obj_dict = {}
for obj in objects:
obj_dict[ obj['id'] ] = obj
pos = 0
for i in range( len(objects) ):
if objects[i]['id'] in subset_ids:
try:
objects[i] = obj_dict[ subset_ids[pos] ]
pos += 1
except KeyError:
raise ValueError('The object with the id "%s"
does '
'not exist.' % subset_ids[pos])
self._objects = tuple(objects)

return counter

security.declarePrivate('getCMFObjectsSubsetIds')
def getCMFObjectsSubsetIds(self, objs):
"""Get the ids of only cmf objects (used for moveObjectsByDelta)
"""
ttool = getToolByName(self, 'portal_types')
cmf_meta_types = [ti.Metatype() for ti in ttool.listTypeInfo()]
return [obj['id'] for obj in objs if obj['meta_type'] in
cmf_meta_types ]

security.declareProtected(permissions.ModifyPortalContent,
'getObjectPosition')
def getObjectPosition(self, id):

objs = list(self._objects)
om = [objs.index(om) for om in objs if om['id']==id ]

if om: # only 1 in list if any
return om[0]

raise NotFound, 'Object %s was not found' % str(id)

security.declareProtected(permissions.ModifyPortalContent,
'moveObjectsUp')
def moveObjectsUp(self, ids, delta=1, RESPONSE=None):
""" Move an object up """

...end of zip...

Do you think this could be the getObjectPosition I am looking for?

>
> For me, this looks like a bug in "plone.app.folder".
> When Plone expects that some folders do not support the next/previous
> functionality (because they are unordered), this functionality
> should be automatically disabled for those folders.
>
> You could try to add as a workaround a line:
>
> if pos is None: return None
>
> after the "pos" assignment in "nextprevious".
>
> If this solves your problem, please file a bug report against
> "plone.app.folder".
>

Yes, this solved the problem, and I will file a bug against
plone.app.folder.

>> By the way: I also googled your function "definedBy". Is it this one
>> (and how is it used)?:

...


> You see here a function with name "definedBy" and arguments
> "name" and "class_". Its name suggests that "definedBy(name, cls)"
> tells you where "name" is defined in the context of "cls".
> The function's docstring tells you that the result is the defining
> class (from which you can ask the module, the class is defined in).
> The docstring also tells you that you can pass an object rather
> than a class and that the object's class is used in this case.

Conceptually a bit advanced for me... But I believe i understand that
the "getObjectPosition" is a function. Is it then also an object that I
could pass to "definedBy"?

> As an example: in order to find out which class
> defines "idsInOrder" after you entered your breakpoint above,
> you could use "!import DUtils; DUtils.definedBy('idsInOrder', ordering)"

Does this mean that "ordering" is the context for "idsInOrder" (from the
line "next = ordering.idsInOrder()[pos + 1]" in nextprevious)? And would
I know that "ordering", as an argument to "definedBy", is a class?


Well, a few maybe silly and off topic questions at the end here :-)

Thank you again Dieter

Dieter Maurer

unread,
Jul 10, 2011, 1:38:28 AM7/10/11
to Børge Kjeldstad, plone...@lists.sourceforge.net
Børge Kjeldstad wrote at 2011-7-9 21:38 +0200:
> ...

>Den 05. juli 2011 07:58, skrev Dieter Maurer:
>> Børge Kjeldstad wrote at 2011-7-4 23:21 +0200:
>>> (Pdb) dir()
>>> ['obj', 'ordering', 'pdb', 'pos', 'self']
>>> ...
>>> (Pdb) pos
>>
>> This proves the hypothesis that "pos" (wrongly) is "None".
>>
>>
>> To find out why, you can use one of the PDB "debug" or "j[ump]" commands.
>>
>Out of some reason I think the "debug" comand made Plone hang. Or at
>least I could hardly do anything more in the terminal window and shut it
>down. But "j[ump]" worked, and I jumped back to the pos assignment in
>nextprevious and found some reference to ATFolder:
>
> >
>/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(31)getNextItem()->None

After you jumped before the "pos" assignment", you must
"step" into the function computing the value. This will
tell you where and how the value is computed.

> ...


> def getObjectPosition(self, id):
>
> objs = list(self._objects)
> om = [objs.index(om) for om in objs if om['id']==id ]
>
> if om: # only 1 in list if any
> return om[0]
>
> raise NotFound, 'Object %s was not found' % str(id)

>...
>
>Do you think this could be the getObjectPosition I am looking for?

No. This method cannot return "None".

--

Børge Kjeldstad

unread,
Jul 11, 2011, 5:41:02 PM7/11/11
to Dieter Maurer, plone...@lists.sourceforge.net

Den 10. juli 2011 07:38, skrev Dieter Maurer:
> After you jumped before the "pos" assignment", you must
> "step" into the function computing the value. This will
> tell you where and how the value is computed.

What about this one :-)

>
/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(25)getNextItem()
-> ordering = self.context.getOrdering()
(Pdb) l


20 return self.context.getNextPreviousEnabled()
21
22 def getNextItem(self, obj):
23 """ return info about the next item in the container """

24 pos = self.context.getObjectPosition(obj.getId())
25 -> ordering = self.context.getOrdering()


26 try:
27 try: # first try `__getitem__`
28 next = ordering[pos + 1]
29 except TypeError:

30 import pdb; pdb.set_trace()
(Pdb) j 23
>
/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(23)getNextItem()
-> """ return info about the next item in the container """
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/Products.Archetypes-1.6.1-py2.6.egg/Products/Archetypes/BaseObject.py(191)getId()
-> def getId(self):
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/Products.Archetypes-1.6.1-py2.6.egg/Products/Archetypes/BaseObject.py(194)getId()
-> return self.id
(Pdb) s
--Return--
>
/usr/local/plone4/buildout-cache/eggs/Products.Archetypes-1.6.1-py2.6.egg/Products/Archetypes/BaseObject.py(194)getId()->'art-against-rape'
-> return self.id
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py(86)getObjectPosition()
-> def getObjectPosition(self, id):

.....

I then opened this file (extract following with getObjectPosition
definition at the end):

/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py
with content:

from zope.component import getAdapter, queryAdapter
from zope.interface import implements
from zope.annotation.interfaces import IAttributeAnnotatable

from AccessControl import ClassSecurityInfo
from AccessControl.Permissions import access_contents_information
from AccessControl.Permissions import manage_properties
from OFS.interfaces import IOrderedContainer
from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2Base, _marker

from plone.folder.cmf import PortalFolderBase
from plone.folder.cmf import ModifyPortalContent

from plone.folder.interfaces import IOrderableFolder
from plone.folder.interfaces import IOrdering
from plone.folder.interfaces import IExplicitOrdering

from webdav.NullResource import NullResource


class OrderedBTreeFolderBase(BTreeFolder2Base):
""" BTree folder base class with ordering support. The ordering
is done by a named adapter (to IOrdering), which makes the policy
changeable. """
implements(IOrderedContainer, IOrderableFolder, IAttributeAnnotatable)

_ordering = u'' # name of adapter defining ordering policy

security = ClassSecurityInfo()

def __nonzero__(self):
""" a folder is something, even if it's empty """
return True

security.declareProtected(access_contents_information, 'getOrdering')
def getOrdering(self):
""" return the currently active ordering adapter for this
folder """
adapter = queryAdapter(self, IOrdering, name=self._ordering)
if adapter is None:
adapter = getAdapter(self, IOrdering)
return adapter

security.declareProtected(manage_properties, 'setOrdering')
def setOrdering(self, ordering=u''):
""" (re)set ordering adapter to be used for this folder """
if ordering:
# make sure the adapter exists...
getAdapter(self, IOrdering, name=ordering)
self._ordering = ordering

# IObjectManager

def _getOb(self, id, default=_marker):
""" Return the named object from the folder. """
try:
return super(OrderedBTreeFolderBase, self)._getOb(id, default)
except KeyError, e:
raise AttributeError(e)

def _setOb(self, id, object):
""" Store the named object in the folder. """
super(OrderedBTreeFolderBase, self)._setOb(id, object)
self.getOrdering().notifyAdded(id) # notify the ordering
adapter

def _delOb(self, id):
""" Remove the named object from the folder. """
super(OrderedBTreeFolderBase, self)._delOb(id)
self.getOrdering().notifyRemoved(id) # notify the ordering
adapter

def objectIds(self, spec=None, ordered=True):
if not ordered:
return super(OrderedBTreeFolderBase, self).objectIds(spec)
ordering = self.getOrdering()
if spec is None:
return ordering.idsInOrder()
else:
ids = super(OrderedBTreeFolderBase, self).objectIds(spec)
idxs = []
for id in ids:
idxs.append((ordering.getObjectPosition(id), id))
return [x[1] for x in sorted(idxs, key=lambda a: a[0])]

# IOrderSupport - mostly deprecated, use the adapter directly instead

security.declareProtected(access_contents_information,

'getObjectPosition')
def getObjectPosition(self, id):

""" Get the position of an object by its id. """
return self.getOrdering().getObjectPosition(id)

security.declareProtected(manage_properties, 'moveObjectsUp')
def moveObjectsUp(self, ids, delta=1, subset_ids=None):


...end of snip...

And then pdb-stepped further:

(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py(88)getObjectPosition()
-> return self.getOrdering().getObjectPosition(id)
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py(36)getOrdering()
-> def getOrdering(self):
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py(38)getOrdering()
-> adapter = queryAdapter(self, IOrdering, name=self._ordering)
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/zope.component-3.7.1-py2.6.egg/zope/component/_api.py(101)queryAdapter()
-> def queryAdapter(object, interface=Interface, name=u'', default=None,
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/zope.component-3.7.1-py2.6.egg/zope/component/_api.py(103)queryAdapter()
-> if context is None:
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/zope.component-3.7.1-py2.6.egg/zope/component/_api.py(104)queryAdapter()
-> return adapter_hook(interface, object, name, default)
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/zope.site-3.6.1-py2.6.egg/zope/site/hooks.py(93)adapter_hook()
-> def adapter_hook(interface, object, name='', default=None):
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/zope.site-3.6.1-py2.6.egg/zope/site/hooks.py(94)adapter_hook()
-> try:
(Pdb)
>
/usr/local/plone4/buildout-cache/eggs/zope.site-3.6.1-py2.6.egg/zope/site/hooks.py(95)adapter_hook()
-> return siteinfo.adapter_hook(interface, object, name, default)
(Pdb)
--Call--
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py(12)__init__()
-> def __init__(self, context):
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py(13)__init__()
-> self.context = context
(Pdb) s
--Return--
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py(13)__init__()->None
-> self.context = context
(Pdb) s
--Return--
>
/usr/local/plone4/buildout-cache/eggs/zope.site-3.6.1-py2.6.egg/zope/site/hooks.py(95)adapter_hook()-><plone.f...40d97510>
-> return siteinfo.adapter_hook(interface, object, name, default)
(Pdb)

...

Then I opened the file
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py

And that is my old friend that returned "None". It's content is:

from Acquisition import aq_base
from zope.interface import implements


from zope.component import adapts
from plone.folder.interfaces import IOrdering, IOrderableFolder


class UnorderedOrdering(object):
""" This implementation provides no ordering. """
implements(IOrdering)
adapts(IOrderableFolder)

def __init__(self, context):
self.context = context

def notifyAdded(self, id):
pass

def notifyRemoved(self, id):
pass

def idsInOrder(self):
return aq_base(self.context).objectIds(ordered=False)

def getObjectPosition(self, id):
return None

...end of file...


Thank you again Dieter!

Børge

Dieter Maurer

unread,
Jul 12, 2011, 1:24:44 AM7/12/11
to Børge Kjeldstad, plone...@lists.sourceforge.net
Børge Kjeldstad wrote at 2011-7-11 23:41 +0200:
> ...
>Then I opened the file
>/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py
>
>And that is my old friend that returned "None". It's content is:
>
>from Acquisition import aq_base
>from zope.interface import implements
>from zope.component import adapts
>from plone.folder.interfaces import IOrdering, IOrderableFolder
>
>
>class UnorderedOrdering(object):
> """ This implementation provides no ordering. """
> implements(IOrdering)
> adapts(IOrderableFolder)
>
> ...

>
> def getObjectPosition(self, id):
> return None
>
>...end of file...

Indeed, there you are.

It shows that Plone's "nextprevious" has a weakness: it should
automatically handle "unordered" folders -- and for the moment fails
to do so. But you have already filed a bug report for this.


Note: an important issue with debugging is when to use "next"
(step over call) and "step" (step into call). You use next to step
over calls that are unlikely to contribute to the analysis of
the current problem. Infrastructure calls (such as adapter lookups)
fall under this category. You use "step" to find out about details
in the called function.
Often, you must step into a function ("getId" in your example) even
when you do not really want to (e.g. when the uninteresting call
is on the same line as an interesting one). In this case,
you can use "return" to quickly leave the called function again.
Things like this will get optimized with experience.

--
Dieter

Reply all
Reply to author
Forward
0 new messages