rapydscript-ns 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.agignore +1 -1
  2. package/.github/workflows/ci.yml +38 -38
  3. package/=template.pyj +5 -5
  4. package/CHANGELOG.md +19 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_GAPS.md +420 -0
  8. package/README.md +153 -29
  9. package/TODO.md +16 -118
  10. package/add-toc-to-readme +2 -2
  11. package/bin/export +75 -75
  12. package/bin/rapydscript +70 -70
  13. package/bin/web-repl-export +102 -102
  14. package/build +2 -2
  15. package/language-service/index.js +237 -8
  16. package/memory/project_string_impl.md +43 -0
  17. package/package.json +1 -1
  18. package/publish.py +37 -37
  19. package/release/baselib-plain-pretty.js +248 -38
  20. package/release/baselib-plain-ugly.js +8 -8
  21. package/release/compiler.js +778 -277
  22. package/release/signatures.json +30 -30
  23. package/session.vim +4 -4
  24. package/setup.cfg +2 -2
  25. package/src/ast.pyj +4 -1
  26. package/src/baselib-builtins.pyj +56 -2
  27. package/src/baselib-containers.pyj +2 -0
  28. package/src/baselib-errors.pyj +7 -3
  29. package/src/baselib-internal.pyj +51 -6
  30. package/src/baselib-str.pyj +5 -3
  31. package/src/compiler.pyj +36 -36
  32. package/src/errors.pyj +30 -30
  33. package/src/lib/aes.pyj +646 -646
  34. package/src/lib/asyncio.pyj +534 -0
  35. package/src/lib/base64.pyj +399 -0
  36. package/src/lib/bisect.pyj +73 -0
  37. package/src/lib/collections.pyj +1 -1
  38. package/src/lib/copy.pyj +120 -120
  39. package/src/lib/csv.pyj +494 -0
  40. package/src/lib/elementmaker.pyj +83 -83
  41. package/src/lib/encodings.pyj +126 -126
  42. package/src/lib/gettext.pyj +569 -569
  43. package/src/lib/heapq.pyj +98 -0
  44. package/src/lib/html.pyj +382 -0
  45. package/src/lib/http/__init__.pyj +98 -0
  46. package/src/lib/http/client.pyj +304 -0
  47. package/src/lib/http/cookies.pyj +236 -0
  48. package/src/lib/itertools.pyj +580 -580
  49. package/src/lib/logging.pyj +672 -0
  50. package/src/lib/math.pyj +193 -193
  51. package/src/lib/operator.pyj +11 -11
  52. package/src/lib/pythonize.pyj +20 -20
  53. package/src/lib/random.pyj +118 -118
  54. package/src/lib/react.pyj +74 -74
  55. package/src/lib/string.pyj +357 -0
  56. package/src/lib/textwrap.pyj +329 -0
  57. package/src/lib/traceback.pyj +63 -63
  58. package/src/lib/urllib/__init__.pyj +14 -0
  59. package/src/lib/urllib/error.pyj +66 -0
  60. package/src/lib/urllib/parse.pyj +475 -0
  61. package/src/lib/urllib/request.pyj +86 -0
  62. package/src/lib/uuid.pyj +77 -77
  63. package/src/monaco-language-service/analyzer.js +5 -2
  64. package/src/monaco-language-service/completions.js +26 -0
  65. package/src/monaco-language-service/diagnostics.js +202 -3
  66. package/src/monaco-language-service/dts.js +550 -550
  67. package/src/monaco-language-service/scope.js +1 -0
  68. package/src/output/comments.pyj +45 -45
  69. package/src/output/exceptions.pyj +201 -201
  70. package/src/output/functions.pyj +152 -6
  71. package/src/output/jsx.pyj +164 -164
  72. package/src/output/loops.pyj +17 -2
  73. package/src/output/modules.pyj +1 -1
  74. package/src/output/operators.pyj +15 -0
  75. package/src/output/stream.pyj +0 -1
  76. package/src/output/treeshake.pyj +182 -182
  77. package/src/output/utils.pyj +72 -72
  78. package/src/parse.pyj +80 -17
  79. package/src/string_interpolation.pyj +72 -72
  80. package/src/tokenizer.pyj +1 -1
  81. package/src/unicode_aliases.pyj +576 -576
  82. package/src/utils.pyj +192 -192
  83. package/test/_import_one.pyj +37 -37
  84. package/test/_import_two/__init__.pyj +11 -11
  85. package/test/_import_two/level2/deep.pyj +4 -4
  86. package/test/_import_two/other.pyj +6 -6
  87. package/test/_import_two/sub.pyj +13 -13
  88. package/test/aes_vectors.pyj +421 -421
  89. package/test/annotations.pyj +80 -80
  90. package/test/async_generators.pyj +144 -0
  91. package/test/asyncio.pyj +307 -0
  92. package/test/base64.pyj +202 -0
  93. package/test/bisect.pyj +178 -0
  94. package/test/csv.pyj +405 -0
  95. package/test/decorators.pyj +77 -77
  96. package/test/docstrings.pyj +39 -39
  97. package/test/elementmaker_test.pyj +45 -45
  98. package/test/float_special.pyj +64 -0
  99. package/test/functions.pyj +151 -151
  100. package/test/generators.pyj +41 -41
  101. package/test/generic.pyj +370 -370
  102. package/test/heapq.pyj +174 -0
  103. package/test/html.pyj +212 -0
  104. package/test/http.pyj +259 -0
  105. package/test/imports.pyj +79 -72
  106. package/test/internationalization.pyj +73 -73
  107. package/test/lint.pyj +164 -164
  108. package/test/logging.pyj +356 -0
  109. package/test/long.pyj +130 -0
  110. package/test/loops.pyj +85 -85
  111. package/test/numpy.pyj +734 -734
  112. package/test/parenthesized_with.pyj +141 -0
  113. package/test/python_compat.pyj +3 -5
  114. package/test/python_modulo.pyj +76 -0
  115. package/test/python_modulo_off.pyj +21 -0
  116. package/test/repl.pyj +121 -121
  117. package/test/scoped_flags.pyj +76 -76
  118. package/test/str.pyj +14 -0
  119. package/test/string.pyj +245 -0
  120. package/test/textwrap.pyj +172 -0
  121. package/test/type_display.pyj +48 -0
  122. package/test/type_enforcement.pyj +164 -0
  123. package/test/unit/index.js +14 -6
  124. package/test/unit/language-service-completions.js +119 -0
  125. package/test/unit/language-service-dts.js +543 -543
  126. package/test/unit/language-service-hover.js +455 -455
  127. package/test/unit/language-service-scope.js +32 -0
  128. package/test/unit/language-service.js +127 -3
  129. package/test/unit/run-language-service.js +17 -3
  130. package/test/unit/web-repl.js +2094 -29
  131. package/test/urllib.pyj +193 -0
  132. package/tools/compile.js +1 -1
  133. package/tools/compiler.d.ts +367 -367
  134. package/tools/completer.js +131 -131
  135. package/tools/embedded_compiler.js +7 -7
  136. package/tools/gettext.js +185 -185
  137. package/tools/ini.js +65 -65
  138. package/tools/msgfmt.js +187 -187
  139. package/tools/repl.js +223 -223
  140. package/tools/test.js +118 -118
  141. package/tools/utils.js +128 -128
  142. package/tools/web_repl.js +95 -95
  143. package/try +41 -41
  144. package/web-repl/env.js +196 -196
  145. package/web-repl/index.html +163 -163
  146. package/web-repl/main.js +1 -1
  147. package/web-repl/prism.css +139 -139
  148. package/web-repl/prism.js +113 -113
  149. package/web-repl/rapydscript.js +224 -224
  150. package/web-repl/sha1.js +25 -25
  151. package/test/omit_function_metadata.pyj +0 -20
@@ -0,0 +1,304 @@
1
+ ###########################################################
2
+ # RapydScript Standard Library
3
+ # http.client — HTTP/HTTPS connections via the Fetch API
4
+ ###########################################################
5
+ #
6
+ # Provides:
7
+ # HTTPConnection(host[, port[, timeout]]) — plain HTTP connection
8
+ # HTTPSConnection(host[, port[, timeout]]) — TLS/HTTPS connection
9
+ # HTTPResponse — response returned by getresponse()
10
+ # HTTPException — base exception
11
+ # NotConnected — no request has been sent yet
12
+ # InvalidURL — malformed URL or host
13
+ # RemoteDisconnected — server closed connection unexpectedly
14
+ # HTTP_PORT = 80
15
+ # HTTPS_PORT = 443
16
+ #
17
+ # Usage (inside an async function):
18
+ # from http.client import HTTPConnection, HTTPSConnection
19
+ #
20
+ # async def fetch_page():
21
+ # conn = HTTPConnection('example.com')
22
+ # conn.request('GET', '/path', headers={'Accept': 'text/html'})
23
+ # resp = await conn.getresponse()
24
+ # print(resp.status, resp.reason)
25
+ # body = await resp.read()
26
+ # conn.close()
27
+ # return body
28
+ #
29
+ # For HTTPS use HTTPSConnection or pass a full https:// URL to HTTPConnection.
30
+ #
31
+ # Note: getresponse() wraps the browser/Node Fetch API and returns a Promise.
32
+ # It must be awaited inside an async function. There is no synchronous version
33
+ # because JavaScript has no synchronous HTTP.
34
+ #
35
+ # Unlike Python's http.client, non-2xx responses do NOT raise an exception —
36
+ # check resp.status yourself (matching the lower-level CPython behaviour).
37
+
38
+ HTTP_PORT = 80
39
+ HTTPS_PORT = 443
40
+
41
+
42
+ class HTTPException(Exception):
43
+ """Base class for http.client exceptions."""
44
+ pass
45
+
46
+
47
+ class NotConnected(HTTPException):
48
+ """No request has been issued on this connection yet."""
49
+ pass
50
+
51
+
52
+ class InvalidURL(HTTPException):
53
+ """The URL or host string is malformed."""
54
+ pass
55
+
56
+
57
+ class RemoteDisconnected(HTTPException):
58
+ """The remote end closed the connection unexpectedly."""
59
+ pass
60
+
61
+
62
+ class HTTPResponse:
63
+ """HTTP response object returned by HTTPConnection.getresponse().
64
+
65
+ Attributes
66
+ ----------
67
+ status : int — HTTP status code (e.g. 200)
68
+ reason : str — HTTP reason phrase (e.g. 'OK')
69
+ headers : dict — lowercase header name → value mapping
70
+ url : str — final URL after any redirects
71
+ msg : dict — alias for headers (CPython compat)
72
+
73
+ Methods
74
+ -------
75
+ read([amt]) — Promise → full response body as str (amt ignored)
76
+ json() — Promise → body parsed as JSON
77
+ getheader(name[, default]) — return header value or default
78
+ getheaders() — list of [name, value] pairs
79
+ close() — mark response as closed (no-op in JS)
80
+ """
81
+
82
+ def __init__(self, status, reason, headers, body_text, url):
83
+ self.status = status
84
+ self.reason = reason
85
+ self.url = url
86
+ self._body = body_text
87
+ self._closed = False
88
+ v"""
89
+ // Normalise headers to a plain null-prototype object so getheader()
90
+ // works with both plain dicts and ρσ_dict (web-repl / dict_literals).
91
+ var _hdrs = Object.create(null);
92
+ if (headers) {
93
+ if (headers.jsmap && typeof headers.jsmap.forEach === 'function') {
94
+ headers.jsmap.forEach(function(v, k) {
95
+ _hdrs[String(k).toLowerCase()] = String(v);
96
+ });
97
+ } else if (typeof headers === 'object') {
98
+ Object.keys(headers).forEach(function(k) {
99
+ if (k !== 'jsmap' && k !== '__class__') {
100
+ _hdrs[k.toLowerCase()] = String(headers[k]);
101
+ }
102
+ });
103
+ }
104
+ }
105
+ this.headers = _hdrs;
106
+ this.msg = _hdrs;
107
+ """
108
+
109
+ def read(self, amt=None):
110
+ """Return the response body as a string (as a resolved Promise)."""
111
+ body = self._body
112
+ return v'Promise.resolve(body)'
113
+
114
+ def json(self):
115
+ """Parse the response body as JSON (as a resolved Promise)."""
116
+ body = self._body
117
+ return v'Promise.resolve(JSON.parse(body))'
118
+
119
+ def getheader(self, name, dflt=None):
120
+ """Return the value of header *name* (case-insensitive), or *dflt*."""
121
+ v"""
122
+ var key = name.toLowerCase();
123
+ return Object.prototype.hasOwnProperty.call(this.headers, key)
124
+ ? this.headers[key]
125
+ : dflt;
126
+ """
127
+
128
+ def getheaders(self):
129
+ """Return a list of [name, value] pairs for all response headers."""
130
+ result = []
131
+ v"""
132
+ var hdrs = this.headers;
133
+ Object.keys(hdrs).forEach(function(k) { result.push([k, hdrs[k]]); });
134
+ """
135
+ return result
136
+
137
+ def close(self):
138
+ self._closed = True
139
+
140
+ @property
141
+ def closed(self):
142
+ return self._closed
143
+
144
+
145
+ class HTTPConnection:
146
+ """An HTTP connection to *host*.
147
+
148
+ Parameters
149
+ ----------
150
+ host : str — hostname (may include port as 'host:port')
151
+ port : int — port number; overrides any port in *host*
152
+ timeout : float — seconds before the request is aborted
153
+
154
+ Call request() to set up a request, then await getresponse() to execute it
155
+ and obtain an HTTPResponse.
156
+ """
157
+
158
+ _scheme = 'http'
159
+
160
+ def __init__(self, host, port=None, timeout=None, source_address=None):
161
+ self.host = host
162
+ self.port = port
163
+ self.timeout = timeout
164
+ self._method = None
165
+ self._path = None
166
+ self._body = None
167
+ v"""this._headers = Object.create(null);"""
168
+
169
+ def _build_url(self, path):
170
+ """Construct the full URL from the stored scheme, host, port, and path."""
171
+ v"""
172
+ var scheme = this._scheme;
173
+ var host = this.host;
174
+ var port = this.port;
175
+ // If path is already absolute, return it directly
176
+ if (path.slice(0, 7) === 'http://' || path.slice(0, 8) === 'https://') {
177
+ return path;
178
+ }
179
+ // Append port unless it is the default for the scheme
180
+ if (port != null) {
181
+ var default_port = scheme === 'https' ? 443 : 80;
182
+ if (port !== default_port) {
183
+ host = host + ':' + port;
184
+ }
185
+ }
186
+ return scheme + '://' + host + path;
187
+ """
188
+
189
+ def set_debuglevel(self, level):
190
+ """Set debug level (no-op in JS)."""
191
+ pass
192
+
193
+ def connect(self):
194
+ """Establish the connection (no-op in JS; fetch handles this)."""
195
+ pass
196
+
197
+ def close(self):
198
+ """Discard any pending request state."""
199
+ self._method = None
200
+ self._path = None
201
+ self._body = None
202
+ v"""this._headers = Object.create(null);"""
203
+
204
+ def putrequest(self, method, url, skip_host=False, skip_accept_encoding=False):
205
+ """Begin a request; use putheader() + endheaders() to add headers."""
206
+ self._method = method
207
+ self._path = url
208
+ self._body = None
209
+ v"""this._headers = Object.create(null);"""
210
+
211
+ def putheader(self, header, argument):
212
+ """Add a single header to the pending request."""
213
+ v"""this._headers[header.toLowerCase()] = String(argument);"""
214
+
215
+ def endheaders(self, body=None):
216
+ """Finalise the request headers and optionally set the body."""
217
+ if body is not None:
218
+ self._body = body
219
+
220
+ def request(self, method, url, body=None, headers={}):
221
+ """Set up a request; call await getresponse() to execute it.
222
+
223
+ Parameters
224
+ ----------
225
+ method : str — HTTP verb ('GET', 'POST', etc.)
226
+ url : str — path (e.g. '/api/items') or full URL
227
+ body : str — request body; also switches method hint to POST
228
+ headers : dict — extra request headers
229
+ """
230
+ self._method = method
231
+ self._path = url
232
+ self._body = body
233
+ v"""
234
+ this._headers = Object.create(null);
235
+ var h = headers;
236
+ if (h) {
237
+ // ρσ_dict (web-repl / dict_literals mode) stores keys in h.jsmap (a Map)
238
+ if (h.jsmap && typeof h.jsmap.forEach === 'function') {
239
+ h.jsmap.forEach(function(v, k) {
240
+ this._headers[String(k).toLowerCase()] = String(v);
241
+ }.bind(this));
242
+ } else if (typeof h === 'object') {
243
+ Object.keys(h).forEach(function(k) {
244
+ if (k !== 'jsmap' && k !== '__class__') {
245
+ this._headers[k.toLowerCase()] = String(h[k]);
246
+ }
247
+ }.bind(this));
248
+ }
249
+ }
250
+ """
251
+
252
+ def getresponse(self):
253
+ """Execute the pending request and return a Promise → HTTPResponse.
254
+
255
+ Raises HTTPException on network errors.
256
+ Non-2xx responses are returned normally (check resp.status yourself).
257
+ """
258
+ method = self._method or 'GET'
259
+ path = self._path or '/'
260
+ body = self._body
261
+ headers = self._headers
262
+ timeout = self.timeout
263
+ v"""
264
+ var url = this._build_url(path);
265
+ var _opts = { method: method, headers: headers };
266
+ if (body != null) { _opts.body = body; }
267
+ var _abort = null;
268
+ if (timeout != null && typeof AbortController !== 'undefined') {
269
+ var _ctrl = new AbortController();
270
+ _opts.signal = _ctrl.signal;
271
+ _abort = setTimeout(function() { _ctrl.abort(); }, timeout * 1000);
272
+ }
273
+ return Promise.resolve()
274
+ .then(function() { return fetch(url, _opts); })
275
+ .then(function(resp) {
276
+ if (_abort) { clearTimeout(_abort); _abort = null; }
277
+ var hdrs = {};
278
+ if (resp.headers && typeof resp.headers.forEach === 'function') {
279
+ resp.headers.forEach(function(v, k) { hdrs[k] = v; });
280
+ }
281
+ return resp.text().then(function(body_text) {
282
+ return new HTTPResponse(resp.status, resp.statusText, hdrs, body_text, resp.url);
283
+ });
284
+ })
285
+ .catch(function(e) {
286
+ if (_abort) { clearTimeout(_abort); _abort = null; }
287
+ if (e instanceof HTTPException) throw e;
288
+ throw new HTTPException(String(e));
289
+ });
290
+ """
291
+
292
+
293
+ class HTTPSConnection(HTTPConnection):
294
+ """An HTTPS connection to *host* (TLS).
295
+
296
+ Identical to HTTPConnection but uses https:// as the default scheme.
297
+ Parameters are the same as HTTPConnection.
298
+ """
299
+
300
+ _scheme = 'https'
301
+
302
+ def __init__(self, host, port=None, timeout=None, source_address=None,
303
+ context=None, check_hostname=None):
304
+ HTTPConnection.__init__(self, host, port, timeout, source_address)
@@ -0,0 +1,236 @@
1
+ ###########################################################
2
+ # RapydScript Standard Library
3
+ # http.cookies — HTTP cookie parsing and generation
4
+ ###########################################################
5
+ #
6
+ # Provides:
7
+ # CookieError — raised when a cookie value cannot be parsed
8
+ # Morsel — represents a single cookie with its attributes
9
+ # SimpleCookie — dict-like container of Morsel objects
10
+ #
11
+ # Usage:
12
+ # from http.cookies import SimpleCookie, CookieError
13
+ #
14
+ # # Parse cookies from a Cookie request header
15
+ # c = SimpleCookie()
16
+ # c.load('session=abc123; user=alice')
17
+ # for name, morsel in c.items():
18
+ # print(name, morsel.value)
19
+ #
20
+ # # Build a Set-Cookie response header
21
+ # c2 = SimpleCookie()
22
+ # c2['token'] = 'xyz'
23
+ # c2['token']['path'] = '/'
24
+ # c2['token']['max-age'] = 3600
25
+ # print(c2.output()) # Set-Cookie: token=xyz; Max-Age=3600; Path=/
26
+
27
+
28
+ class CookieError(Exception):
29
+ """Raised when a cookie header cannot be parsed."""
30
+ pass
31
+
32
+
33
+ # Attribute keys for Morsel (lowercase, matching the Set-Cookie directive names)
34
+ _MORSEL_ATTRS = ['expires', 'path', 'comment', 'domain', 'max-age',
35
+ 'secure', 'httponly', 'version', 'samesite']
36
+
37
+
38
+ class Morsel:
39
+ """A single cookie name/value pair plus its Set-Cookie attributes.
40
+
41
+ Attributes
42
+ ----------
43
+ key : str — cookie name
44
+ value : str — decoded cookie value
45
+ coded_value : str — value as it appears in the header (same as value here)
46
+
47
+ Cookie attributes (set via morsel['path'] = '/', etc.):
48
+ 'expires', 'path', 'comment', 'domain', 'max-age',
49
+ 'secure', 'httponly', 'version', 'samesite'
50
+ """
51
+
52
+ def __init__(self):
53
+ self.key = None
54
+ self.value = None
55
+ self.coded_value = None
56
+ self._attrs = {}
57
+
58
+ def set(self, key, val, coded_val):
59
+ self.key = key
60
+ self.value = val
61
+ self.coded_value = coded_val
62
+
63
+ def __getitem__(self, k):
64
+ v"""
65
+ var lk = k.toLowerCase();
66
+ return Object.prototype.hasOwnProperty.call(this._attrs, lk)
67
+ ? this._attrs[lk]
68
+ : '';
69
+ """
70
+
71
+ def __setitem__(self, k, v):
72
+ v"""
73
+ var lk = k.toLowerCase();
74
+ this._attrs[lk] = v;
75
+ """
76
+
77
+ def isReservedKey(self, k):
78
+ v"""
79
+ var lk = k.toLowerCase();
80
+ for (var i = 0; i < _MORSEL_ATTRS.length; i++) {
81
+ if (_MORSEL_ATTRS[i] === lk) return true;
82
+ }
83
+ return false;
84
+ """
85
+
86
+ def OutputString(self):
87
+ """Return the cookie string (without the 'Set-Cookie:' prefix)."""
88
+ v"""
89
+ var parts = [this.key + '=' + this.coded_value];
90
+ var a = this._attrs;
91
+ if (a['expires']) { parts.push('expires=' + a['expires']); }
92
+ if (a['path']) { parts.push('Path=' + a['path']); }
93
+ if (a['comment']) { parts.push('Comment=' + a['comment']); }
94
+ if (a['domain']) { parts.push('Domain=' + a['domain']); }
95
+ if (a['max-age'] !== undefined && a['max-age'] !== '') {
96
+ parts.push('Max-Age=' + a['max-age']);
97
+ }
98
+ if (a['version']) { parts.push('Version=' + a['version']); }
99
+ if (a['samesite']) { parts.push('SameSite=' + a['samesite']); }
100
+ if (a['secure']) { parts.push('Secure'); }
101
+ if (a['httponly']) { parts.push('HttpOnly'); }
102
+ return parts.join('; ');
103
+ """
104
+
105
+ def output(self, header='Set-Cookie'):
106
+ return header + ': ' + self.OutputString()
107
+
108
+ def __str__(self):
109
+ return self.OutputString()
110
+
111
+ def __repr__(self):
112
+ return '<Morsel: ' + str(self.key) + '=' + str(self.coded_value) + '>'
113
+
114
+
115
+ def _parse_cookie_str(rawdata):
116
+ """Parse a Cookie or Set-Cookie header string into a dict of name→Morsel."""
117
+ result = {}
118
+ v"""
119
+ // Split on ';' to get individual name=value pairs.
120
+ // We only parse the name=value portion here (not Set-Cookie attributes);
121
+ // for SimpleCookie we treat each ';'-separated segment as a cookie if it
122
+ // contains '=', otherwise we skip it (it's a directive like 'HttpOnly').
123
+ var parts = String(rawdata).split(';');
124
+ for (var i = 0; i < parts.length; i++) {
125
+ var part = parts[i].replace(/^\s+|\s+$/g, '');
126
+ if (part.length === 0) continue;
127
+ var eq = part.indexOf('=');
128
+ if (eq < 0) continue;
129
+ var name = part.slice(0, eq).replace(/^\s+|\s+$/g, '');
130
+ var val = part.slice(eq + 1).replace(/^\s+|\s+$/g, '');
131
+ // Strip surrounding quotes if present
132
+ if (val.length >= 2 && val.charAt(0) === '"' && val.charAt(val.length - 1) === '"') {
133
+ val = val.slice(1, val.length - 1);
134
+ }
135
+ if (name.length === 0) continue;
136
+ var m = new Morsel();
137
+ m.set(name, val, val);
138
+ result[name] = m;
139
+ }
140
+ """
141
+ return result
142
+
143
+
144
+ class SimpleCookie:
145
+ """A dict-like container of Morsel objects parsed from cookie headers.
146
+
147
+ cookie['name'] = 'value' — set a cookie (creates/replaces a Morsel)
148
+ cookie['name'] — get the Morsel for 'name'
149
+ cookie['name']['path'] = '/'— set a cookie attribute
150
+
151
+ load(rawdata) — parse cookie string into this container
152
+ output(header, sep) — format all cookies as Set-Cookie lines
153
+ items() — [(name, morsel), ...]
154
+ keys() — [name, ...]
155
+ values() — [morsel, ...]
156
+ """
157
+
158
+ def __init__(self, input=None):
159
+ self._cookies = {}
160
+ if input is not None:
161
+ self.load(input)
162
+
163
+ def load(self, rawdata):
164
+ """Parse *rawdata* (a cookie header string) and merge into this cookie."""
165
+ parsed = _parse_cookie_str(rawdata)
166
+ v"""
167
+ Object.keys(parsed).forEach(function(k) {
168
+ this._cookies[k] = parsed[k];
169
+ }.bind(this));
170
+ """
171
+
172
+ def __getitem__(self, key):
173
+ v"""
174
+ if (!Object.prototype.hasOwnProperty.call(this._cookies, key)) {
175
+ throw new KeyError(key);
176
+ }
177
+ return this._cookies[key];
178
+ """
179
+
180
+ def __setitem__(self, key, val):
181
+ v"""
182
+ if (!Object.prototype.hasOwnProperty.call(this._cookies, key)) {
183
+ var m = new Morsel();
184
+ m.set(key, String(val), String(val));
185
+ this._cookies[key] = m;
186
+ } else {
187
+ this._cookies[key].value = String(val);
188
+ this._cookies[key].coded_value = String(val);
189
+ }
190
+ """
191
+
192
+ def __contains__(self, key):
193
+ v"""return Object.prototype.hasOwnProperty.call(this._cookies, key);"""
194
+
195
+ def keys(self):
196
+ v"""return Object.keys(this._cookies);"""
197
+
198
+ def values(self):
199
+ v"""
200
+ var out = [];
201
+ Object.keys(this._cookies).forEach(function(k) { out.push(this._cookies[k]); }.bind(this));
202
+ return out;
203
+ """
204
+
205
+ def items(self):
206
+ v"""
207
+ var out = [];
208
+ Object.keys(this._cookies).forEach(function(k) {
209
+ out.push([k, this._cookies[k]]);
210
+ }.bind(this));
211
+ return out;
212
+ """
213
+
214
+ def output(self, header='Set-Cookie:', sep='\r\n'):
215
+ """Return all cookies formatted as Set-Cookie header lines."""
216
+ v"""
217
+ var lines = [];
218
+ var ck = this._cookies;
219
+ Object.keys(ck).forEach(function(k) {
220
+ lines.push(ck[k].output(header.replace(/:$/, '')));
221
+ });
222
+ return lines.join(sep);
223
+ """
224
+
225
+ def __str__(self):
226
+ return self.output()
227
+
228
+ def __repr__(self):
229
+ v"""
230
+ var parts = [];
231
+ var ck = this._cookies;
232
+ Object.keys(ck).forEach(function(k) {
233
+ parts.push(JSON.stringify(k) + ': ' + JSON.stringify(ck[k].coded_value));
234
+ });
235
+ return '<SimpleCookie: ' + parts.join(', ') + '>';
236
+ """