annotate roundup/cgi/TAL/TALParser.py @ 5710:0b79bfcb3312

Add support for making an idempotent POST. This allows retrying a POST that was interrupted. It involves creating a post once only (poe) url /rest/data/<class>/@poe/<random_token>. This url acts the same as a post to /rest/data/<class>. However once the @poe url is used, it can't be used for a second POST. To make these changes: 1) Take the body of post_collection into a new post_collection_inner function. Have post_collection call post_collection_inner. 2) Add a handler for POST to rest/data/class/@poe. This will return a unique POE url. By default the url expires after 30 minutes. The POE random token is only good for a specific user and is stored in the session db. 3) Add a handler for POST to rest/data/<class>/@poe/<random token>. The random token generated in 2 is validated for proper class (if token is not generic) and proper user and must not have expired. If everything is valid, call post_collection_inner to process the input and generate the new entry. To make recognition of 2 stable (so it's not confused with rest/data/<:class_name>/<:item_id>), removed @ from Routing::url_to_regex. The current Routing.execute method stops on the first regular expression to match the URL. Since item_id doesn't accept a POST, I was getting 405 bad method sometimes. My guess is the order of the regular expressions is not stable, so sometime I would get the right regexp for /data/<class>/@poe and sometime I would get the one for /data/<class>/<item_id>. By removing the @ from the url_to_regexp, there was no way for the item_id case to match @poe. There are alternate fixes we may need to look at. If a regexp matches but the method does not, return to the regexp matching loop in execute() looking for another match. Only once every possible match has failed should the code return a 405 method failure. Another fix is to implement a more sophisticated mechanism so that @Routing.route("/data/<:class_name>/<:item_id>/<:attr_name>", 'PATCH') has different regexps for matching <:class_name> <:item_id> and <:attr_name>. Currently the regexp specified by url_to_regex is used for every component. Other fixes: Made failure to find any props in props_from_args return an empty dict rather than throwing an unhandled error. Make __init__ for SimulateFieldStorageFromJson handle an empty json doc. Useful for POSTing to rest/data/class/@poe with an empty document. Testing: added testPostPOE to test/rest_common.py that I think covers all the code that was added. Documentation: Add doc to rest.txt in the "Client API" section titled: Safely Re-sending POST". Move existing section "Adding new rest endpoints" in "Client API" to a new second level section called "Programming the REST API". Also a minor change to the simple rest client moving the header setting to continuation lines rather than showing one long line.
author John Rouillard <rouilj@ieee.org>
date Sun, 14 Apr 2019 21:07:11 -0400
parents 23b8e6067f7c
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
1 ##############################################################################
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
2 #
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
3 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
4 # All Rights Reserved.
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
5 #
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
6 # This software is subject to the provisions of the Zope Public License,
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
7 # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
8 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
9 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
10 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
11 # FOR A PARTICULAR PURPOSE.
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
12 #
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
13 ##############################################################################
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
14 """
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
15 Parse XML and compile to TALInterpreter intermediate code.
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
16 """
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
17
5388
d26921b851c3 Python 3 preparation: make relative imports explicit.
Joseph Myers <jsm@polyomino.org.uk>
parents: 5377
diff changeset
18 from .XMLParser import XMLParser
d26921b851c3 Python 3 preparation: make relative imports explicit.
Joseph Myers <jsm@polyomino.org.uk>
parents: 5377
diff changeset
19 from .TALDefs import XML_NS, ZOPE_I18N_NS, ZOPE_METAL_NS, ZOPE_TAL_NS
d26921b851c3 Python 3 preparation: make relative imports explicit.
Joseph Myers <jsm@polyomino.org.uk>
parents: 5377
diff changeset
20 from .TALGenerator import TALGenerator
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
21
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
22 class TALParser(XMLParser):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
23
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
24 ordered_attributes = 1
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
25
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
26 def __init__(self, gen=None): # Override
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
27 XMLParser.__init__(self)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
28 if gen is None:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
29 gen = TALGenerator()
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
30 self.gen = gen
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
31 self.nsStack = []
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
32 self.nsDict = {XML_NS: 'xml'}
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
33 self.nsNew = []
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
34
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
35 def getCode(self):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
36 return self.gen.getCode()
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
37
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
38 def getWarnings(self):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
39 return ()
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
40
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
41 def StartNamespaceDeclHandler(self, prefix, uri):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
42 self.nsStack.append(self.nsDict.copy())
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
43 self.nsDict[uri] = prefix
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
44 self.nsNew.append((prefix, uri))
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
45
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
46 def EndNamespaceDeclHandler(self, prefix):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
47 self.nsDict = self.nsStack.pop()
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
48
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
49 def StartElementHandler(self, name, attrs):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
50 if self.ordered_attributes:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
51 # attrs is a list of alternating names and values
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
52 attrlist = []
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
53 for i in range(0, len(attrs), 2):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
54 key = attrs[i]
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
55 value = attrs[i+1]
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
56 attrlist.append((key, value))
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
57 else:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
58 # attrs is a dict of {name: value}
5395
23b8e6067f7c Python 3 preparation: update calls to dict methods.
Joseph Myers <jsm@polyomino.org.uk>
parents: 5388
diff changeset
59 attrlist = sorted(attrs.items()) # Sorted for definiteness
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
60 name, attrlist, taldict, metaldict, i18ndict \
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
61 = self.process_ns(name, attrlist)
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
62 attrlist = self.xmlnsattrs() + attrlist
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
63 self.gen.emitStartElement(name, attrlist, taldict, metaldict, i18ndict)
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
64
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
65 def process_ns(self, name, attrlist):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
66 taldict = {}
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
67 metaldict = {}
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
68 i18ndict = {}
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
69 fixedattrlist = []
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
70 name, namebase, namens = self.fixname(name)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
71 for key, value in attrlist:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
72 key, keybase, keyns = self.fixname(key)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
73 ns = keyns or namens # default to tag namespace
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
74 item = key, value
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
75 if ns == 'metal':
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
76 metaldict[keybase] = value
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
77 item = item + ("metal",)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
78 elif ns == 'tal':
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
79 taldict[keybase] = value
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
80 item = item + ("tal",)
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
81 elif ns == 'i18n':
5377
12fe83f90f0d Python 3 preparation: use repr() instead of ``.
Joseph Myers <jsm@polyomino.org.uk>
parents: 2348
diff changeset
82 assert 0, "dealing with i18n: " + repr((keybase, value))
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
83 i18ndict[keybase] = value
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
84 item = item + ('i18n',)
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
85 fixedattrlist.append(item)
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
86 if namens in ('metal', 'tal', 'i18n'):
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
87 taldict['tal tag'] = namens
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
88 return name, fixedattrlist, taldict, metaldict, i18ndict
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
89
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
90 def xmlnsattrs(self):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
91 newlist = []
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
92 for prefix, uri in self.nsNew:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
93 if prefix:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
94 key = "xmlns:" + prefix
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
95 else:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
96 key = "xmlns"
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
97 if uri in (ZOPE_METAL_NS, ZOPE_TAL_NS, ZOPE_I18N_NS):
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
98 item = (key, uri, "xmlns")
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
99 else:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
100 item = (key, uri)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
101 newlist.append(item)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
102 self.nsNew = []
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
103 return newlist
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
104
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
105 def fixname(self, name):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
106 if ' ' in name:
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
107 uri, name = name.split(' ')
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
108 prefix = self.nsDict[uri]
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
109 prefixed = name
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
110 if prefix:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
111 prefixed = "%s:%s" % (prefix, name)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
112 ns = 'x'
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
113 if uri == ZOPE_TAL_NS:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
114 ns = 'tal'
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
115 elif uri == ZOPE_METAL_NS:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
116 ns = 'metal'
2348
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
117 elif uri == ZOPE_I18N_NS:
8c2402a78bb0 beginning getting ZPT up to date: TAL first
Richard Jones <richard@users.sourceforge.net>
parents: 2005
diff changeset
118 ns = 'i18n'
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
119 return (prefixed, name, ns)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
120 return (name, name, None)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
121
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
122 def EndElementHandler(self, name):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
123 name = self.fixname(name)[0]
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
124 self.gen.emitEndElement(name)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
125
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
126 def DefaultHandler(self, text):
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
127 self.gen.emitRawText(text)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
128
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
129 def test():
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
130 import sys
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
131 p = TALParser()
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
132 file = "tests/input/test01.xml"
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
133 if sys.argv[1:]:
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
134 file = sys.argv[1]
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
135 p.parseFile(file)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
136 program, macros = p.getCode()
5388
d26921b851c3 Python 3 preparation: make relative imports explicit.
Joseph Myers <jsm@polyomino.org.uk>
parents: 5377
diff changeset
137 from .TALInterpreter import TALInterpreter
d26921b851c3 Python 3 preparation: make relative imports explicit.
Joseph Myers <jsm@polyomino.org.uk>
parents: 5377
diff changeset
138 from .DummyEngine import DummyEngine
1049
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
139 engine = DummyEngine(macros)
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
140 TALInterpreter(program, macros, engine, sys.stdout, wrap=0)()
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
141
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
142 if __name__ == "__main__":
Richard Jones <richard@users.sourceforge.net>
parents:
diff changeset
143 test()

Roundup Issue Tracker: http://roundup-tracker.org/