diff options
| author | eunhhu <[email protected]> | 2026-09-24 17:14:54 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-24 17:14:54 +0100 |
| commit | b1ce1927022d374198441afafeb27199fc8a052a (patch) | |
| tree | 6021f175f4c67ed03b44240eaa2f90d846f12f6f /prototypes/connections/examples/select-text.py | |
| parent | d34f9426d5317a1948f12f229439946728bc342a (diff) | |
| download | pure-b1ce1927022d374198441afafeb27199fc8a052a.tar.gz pure-b1ce1927022d374198441afafeb27199fc8a052a.zip | |
Establish native micro runtime checkpoint
Diffstat (limited to 'prototypes/connections/examples/select-text.py')
| -rw-r--r-- | prototypes/connections/examples/select-text.py | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/prototypes/connections/examples/select-text.py b/prototypes/connections/examples/select-text.py new file mode 100644 index 0000000..ffaf80b --- /dev/null +++ b/prototypes/connections/examples/select-text.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""Save a UTF-8 document selection through selection.pure (character indices).""" +import argparse +import http.client +import json +from pathlib import Path +import re +import struct +from urllib.parse import urlsplit + +LIMIT = (2 * 6 + 32) * 32768 + 8192 + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('file', type=Path) + parser.add_argument('--key', required=True) + parser.add_argument('--start', type=int, required=True, help='first Unicode code point, starting at 0') + parser.add_argument('--end', type=int, required=True, help='exclusive Unicode code point boundary') + parser.add_argument('--revision', type=int, default=0, help='0 creates; otherwise expected stored revision') + parser.add_argument('--url', default='http://127.0.0.1:8767') + args = parser.parse_args() + url = urlsplit(args.url) + try: + port = url.port or 80 + except ValueError as error: + parser.error(str(error)) + if (url.scheme != 'http' or url.hostname not in ['localhost', '127.0.0.1'] or + url.username is not None or url.password is not None or url.path not in ['', '/'] or + url.query or url.fragment): + parser.error('use a local HTTP server origin') + if not re.fullmatch(r'[A-Za-z0-9_-]{1,64}', args.key) or not 0 <= args.revision <= 2**63 - 1: + parser.error('invalid object key or revision') + try: + with args.file.open('rb') as source: + document = source.read(LIMIT - 8 + 1) + if len(document) > LIMIT - 8: + parser.error('document exceeds the prototype request limit') + text = document.decode('utf-8') + except (OSError, UnicodeError) as error: + parser.error(str(error)) + if not 0 <= args.start <= args.end <= len(text): + parser.error('selection is outside the document') + start = len(text[:args.start].encode('utf-8')) + selected = text[args.start:args.end].encode('utf-8') + # The boundary layout is explicit; storage receives only selected bytes. + payload = struct.pack('<II', start, len(selected)) + document + headers = {'Content-Type': 'application/octet-stream'} + headers['If-None-Match' if args.revision == 0 else 'If-Match'] = ( + '*' if args.revision == 0 else f'"{args.revision}"') + connection = http.client.HTTPConnection(url.hostname, port, timeout=12) + try: + connection.request('PUT', '/objects/' + args.key, payload, headers) + response = connection.getresponse() + body = response.read(LIMIT + 1) + if response.status != 200: + parser.error(f'storage response {response.status}: {response.getheader("X-Pure-Error")}') + if body != selected: + parser.error('response differs from the selection; check the installed program (write may have occurred)') + revision = response.getheader('ETag', '') + if not re.fullmatch(r'"[1-9][0-9]*"', revision) or int(revision[1:-1]) > 2**63 - 1: + parser.error('response has no valid stored revision (write may have occurred)') + except (OSError, http.client.HTTPException) as error: + parser.error(f'{error}; storage outcome is unknown if the request was sent') + finally: + connection.close() + print(json.dumps({'revision': revision[1:-1], 'selected': selected.decode('utf-8'), + 'stored_bytes': len(selected)}, ensure_ascii=False)) + + +if __name__ == '__main__': + main() |
