mò ,á¸Ec@s(dklZdklZdklZdklZdfd„ƒYZeƒZ dfd„ƒYZ e ƒZ d„Z d fd „ƒYZ d fd „ƒYZd fd„ƒYZdefd„ƒYZeZdgZd„Zdfd„ƒYZdefd„ƒYZd„ZdklZd„ZdS((ssetDefaultSkin(sIDefaultBrowserLayer(sRetry(s ConflictErrortTracercBs;tZdZd„Zd„Zd„Zd„Zd„ZRS(s™Trace used to record pathway taken through the publisher machinery. And provide framework for spewing out exceptions at just the right time. cCs|iƒdS(N(tselftreset(R((t;/data/zmath/zope/lib/python/ZPublisher/tests/testPublish.pyt__init__ scCsg|_h|_dS(N(Rt tracedPatht exceptions(R((RRs cCs|ii|ƒdS(N(RRtappendtarg(RR((RRscCsx|iD] }|GHq WdS(N(RRR(RR((RtshowTracedPaths cCsqtii|dƒ}|oQ|d}|i|ƒt|ƒidƒd}|i d||fƒ|‚ndS(Nit.iÿÿÿÿsraising %s from %s( ttracerRtgettcontexttNonet exceptiontremovetstrtsplittexceptionShortNameRR(RR RRR((RtpossiblyRaiseExceptions  (t__name__t __module__t__doc__RRRR R(((RRs     tTransactionsManagercBs2tZdZd„Zd„Zd„Zd„ZRS(sRMock TransactionManager to replace Zope2.App.startup.TransactionsManager. cCstidƒdS(Ntabort(R R(R((RR+scCstidƒdS(Ntbegin(R R(R((RR.scCstidƒtidƒdS(Ntcommit(R RR(R((RR1s cCsdS(N((Rtobjtrequest((RtrecordMetaData5s(RRRRRRR(((RR&s    cCs_t|tƒot|||ƒ‚n|tjo|iƒntidƒti dƒdS(s^Mock zpublisher_exception_hook to replace Zope2.App.startup.zpublisher_exception_hook tzpublisher_exception_hookN( t issubclassttt ConflictErrortRetrytvt tracebacktreraiseR RR(t publishedRR!R$R%((RR:s   tObjectcBstZdZd„ZRS(s#Mock object for traversing to. cCstidƒtidƒdS(Nt__call__(R RR(R((RR)Ks  (RRRR)(((RR(Gs tResponsecBstZdZd„ZRS(sCMock Response to replace ZPublisher.HTTPResponse.HTTPResponse. cCsdS(N((Rta((RtsetBodyTs(RRRR,(((RR*Ps tRequestcBsktZdZfZd„Zd„Zdd„Zd„Zd„Zd„Z dZ d Z d „Z d „Z RS( s@Mock Request to replace ZPublisher.HTTPRequest.HTTPRequest. cCstƒ|_dS(N(R*Rtresponse(R((RR]scCsdS(N((R((Rt processInputs`stcCsdS(NR0((RR+tb((RR cscCsdS(N((Rtnametvalue((Rt __setitem__fscCstƒS(N(R((Rtpathtvalidated_hook((RttraverseiscCsdS(N((R((RtcloselsiicCs|i|ijS(N(Rt retry_counttretry_max_count(R((Rtsupports_retryrscCs+|id7_|iƒ}|i|_|S(Ni(RR9t __class__tr(RR=((Rtretryus  (RRRtargsRR/R R4R7R8R9R:R;R>(((RR-Ws        tRequestWithSkinCheckcBstZd„ZRS(NcCs6ti|ƒo tƒSntgtid>> from ZPublisher.Publish import publish ZPublisher will commit the transaction after it has made a rendering of the object. >>> tracer.reset() >>> request = Request() >>> response = publish(request, module_name, after_list) >>> tracer.showTracedPath() begin __call__ commit If ZPublisher sees an exception when rendering the requested object then it will try rendering an error message. The transaction is eventually aborted after rendering the error message. (Note that this handling of the transaction boundaries is different to how Zope3 does things. Zope3 aborts the transaction before rendering the error message.) >>> tracer.reset() >>> tracer.exceptions['__call__'] = [ValueError] >>> request = Request() >>> response = publish(request, module_name, after_list) >>> tracer.showTracedPath() begin __call__ raising ValueError from __call__ zpublisher_exception_hook abort If there is a futher exception raised while trying to render the error then ZPublisher is still required to abort the transaction. And the exception propagates out of publish. >>> tracer.reset() >>> tracer.exceptions['__call__'] = [ValueError] >>> tracer.exceptions['zpublisher_exception_hook'] = [ValueError] >>> request = Request() >>> response = publish(request, module_name, after_list) Traceback (most recent call last): ... ValueError >>> tracer.showTracedPath() begin __call__ raising ValueError from __call__ zpublisher_exception_hook raising ValueError from zpublisher_exception_hook abort ZPublisher can also deal with database ConflictErrors. The original transaction is aborted and a second is made in which the request is attempted again. (There is a fair amount of collaboration to implement the retry functionality. Relies on Request and zpublisher_exception_hook also doing the right thing.) >>> tracer.reset() >>> tracer.exceptions['__call__'] = [ConflictError] >>> request = Request() >>> response = publish(request, module_name, after_list) >>> tracer.showTracedPath() begin __call__ raising ConflictError from __call__ abort begin __call__ commit Same behaviour if there is a conflict when attempting to commit the transaction. (Again this relies on collaboration from zpublisher_exception_hook.) >>> tracer.reset() >>> tracer.exceptions['commit'] = [ConflictError] >>> request = Request() >>> response = publish(request, module_name, after_list) >>> tracer.showTracedPath() begin __call__ commit raising ConflictError from commit abort begin __call__ commit ZPublisher will retry the request several times. After 3 retries it gives up and the exception propogates out. >>> tracer.reset() >>> tracer.exceptions['__call__'] = [ConflictError, ConflictError, ... ConflictError, ConflictError] >>> request = Request() >>> response = publish(request, module_name, after_list) Traceback (most recent call last): ... ConflictError: database conflict error >>> tracer.showTracedPath() begin __call__ raising ConflictError from __call__ abort begin __call__ raising ConflictError from __call__ abort begin __call__ raising ConflictError from __call__ abort begin __call__ raising ConflictError from __call__ abort However ZPublisher does not retry ConflictErrors that are raised while trying to render an error message. >>> tracer.reset() >>> tracer.exceptions['__call__'] = [ValueError] >>> tracer.exceptions['zpublisher_exception_hook'] = [ConflictError] >>> request = Request() >>> response = publish(request, module_name, after_list) Traceback (most recent call last): ... ConflictError: database conflict error >>> tracer.showTracedPath() begin __call__ raising ValueError from __call__ zpublisher_exception_hook raising ConflictError from zpublisher_exception_hook abort The request generator applies the default skin layer to the request. We have a specially crafted request that tests this. If the request does not have the required interface it raises an ValueError. Let's see that this works as expected >>> tracer.reset() >>> request = RequestWithSkinCheck() >>> setDefaultSkin(request) >>> response = publish(request, module_name, after_list) >>> tracer.showTracedPath() begin __call__ commit Retries generate new request objects, the publisher needs to ensure that the skin layer is applied to those as well. If the skin layer is not applied to subsequent requests, an ValueError would be raised here. >>> tracer.reset() >>> tracer.exceptions['commit'] = [ConflictError, ConflictError, ... ConflictError, ConflictError] >>> request = RequestWithSkinCheck() >>> setDefaultSkin(request) >>> response = publish(request, module_name, after_list) Traceback (most recent call last): ... ConflictError: database conflict error >>> tracer.showTracedPath() begin __call__ commit raising ConflictError from commit abort begin __call__ commit raising ConflictError from commit abort begin __call__ commit raising ConflictError from commit abort begin __call__ commit raising ConflictError from commit abort N((((Rt testPublisher‡s¿tObjectNotFoundcBstZdZd„ZRS(s#Mock object for traversing to. cCstidƒdS(NRE(R R(R((RR)Ms (RRRR)(((RREIs t PathRequestcBs&tZd„Zdd„Zd„ZRS(NcCs||_ti|ƒdS(N(R5Rt PATH_INFOR-R(RR5((RRSs R0cCs |djo |iSndSdS(NRGR0(R+RRG(RR+R1((RR Ws  cCs&||ijo tƒSntƒSdS(N(R5RRGR(RE(RR5R6((RR7]s (RRRR R7(((RRFRs  cCsdS(s Tests to ensure that publish passes paths through to the request without stripping spaces (as this can lead to google indexing pages with a trailing space when someone has a typo in an href to you're site). Zope bug #1991. >>> from ZPublisher.Publish import publish Without the trailing space, should work normally >>> tracer.reset() >>> request = PathRequest('/foo') >>> response = publish(request, module_name, after_list) >>> tracer.showTracedPath() begin __call__ commit Now with a trailing space, should also work normally, but in zope 2.9.0 and earlier publish did a strip() on the path so instead of __call__ you an ObjectNotFound in the trace. >>> tracer.reset() >>> request = PathRequest('/foo ') >>> response = publish(request, module_name, after_list) >>> tracer.showTracedPath() begin __call__ commit N((((RttestPublishPathcs(sdoctestcCs tiƒS(N(tdoctestt DocTestSuite(((Rt test_suite‡sN(tzope.publisher.browsertsetDefaultSkint!zope.publisher.interfaces.browserRAt ZPublisherR#tZODB.POSExceptionR"RR Rtzpublisher_transactions_managerRR(R*R-R@Rt module_nameRt after_listRDRERFRHt zope.testingRIRK(R#RAR"RFRSRQR(RMRRDRER*R-RHRKRRR@RIRRR ((Rt?s(        $    "