omlish 0.0.0.dev423__py3-none-any.whl → 0.0.0.dev484__py3-none-any.whl
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.
Potentially problematic release.
This version of omlish might be problematic. Click here for more details.
- omlish/{.manifests.json → .omlish-manifests.json} +12 -0
- omlish/__about__.py +21 -18
- omlish/argparse/all.py +17 -9
- omlish/argparse/cli.py +16 -3
- omlish/argparse/utils.py +21 -0
- omlish/asyncs/all.py +0 -13
- omlish/asyncs/asyncio/rlock.py +110 -0
- omlish/asyncs/asyncio/subprocesses.py +2 -2
- omlish/asyncs/asyncio/sync.py +43 -0
- omlish/asyncs/asyncio/utils.py +2 -0
- omlish/asyncs/ioproxy/proxy.py +1 -1
- omlish/asyncs/sync.py +25 -0
- omlish/bootstrap/_marshal.py +1 -1
- omlish/bootstrap/diag.py +12 -21
- omlish/bootstrap/main.py +2 -5
- omlish/bootstrap/sys.py +27 -28
- omlish/c3.py +4 -1
- omlish/cexts/include/omlish/omlish.hh +1 -0
- omlish/collections/__init__.py +13 -1
- omlish/collections/attrregistry.py +210 -0
- omlish/collections/cache/impl.py +3 -2
- omlish/collections/identity.py +1 -0
- omlish/collections/mappings.py +28 -0
- omlish/collections/trie.py +5 -1
- omlish/collections/utils.py +77 -0
- omlish/concurrent/__init__.py +0 -11
- omlish/concurrent/all.py +18 -0
- omlish/concurrent/futures.py +25 -0
- omlish/concurrent/threadlets.py +1 -1
- omlish/configs/processing/flattening.py +1 -1
- omlish/configs/processing/merging.py +8 -6
- omlish/configs/types.py +1 -1
- omlish/daemons/__init__.py +70 -0
- omlish/daemons/daemon.py +2 -2
- omlish/daemons/launching.py +2 -2
- omlish/daemons/reparent.py +2 -3
- omlish/daemons/spawning.py +2 -3
- omlish/dataclasses/__init__.py +5 -5
- omlish/dataclasses/errors.py +1 -1
- omlish/dataclasses/impl/api/classes/decorator.py +3 -0
- omlish/dataclasses/impl/api/classes/make.py +4 -1
- omlish/dataclasses/impl/concerns/doc.py +1 -1
- omlish/dataclasses/impl/concerns/repr.py +15 -2
- omlish/dataclasses/impl/configs.py +97 -36
- omlish/dataclasses/impl/generation/compilation.py +21 -19
- omlish/dataclasses/impl/generation/globals.py +1 -0
- omlish/dataclasses/impl/generation/ops.py +1 -0
- omlish/dataclasses/impl/generation/processor.py +105 -24
- omlish/dataclasses/impl/processing/base.py +8 -0
- omlish/dataclasses/impl/processing/driving.py +8 -8
- omlish/dataclasses/specs.py +34 -2
- omlish/dataclasses/tools/as_.py +0 -12
- omlish/dataclasses/tools/modifiers.py +5 -0
- omlish/dataclasses/tools/static.py +1 -1
- omlish/diag/_pycharm/runhack.py +1 -1
- omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
- omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
- omlish/diag/{ps.py → cmds/ps.py} +6 -6
- omlish/diag/procfs.py +4 -4
- omlish/diag/pycharm.py +16 -2
- omlish/diag/pydevd.py +58 -40
- omlish/diag/replserver/console.py +3 -3
- omlish/diag/replserver/server.py +2 -2
- omlish/dispatch/__init__.py +18 -12
- omlish/dispatch/methods.py +50 -140
- omlish/dom/rendering.py +1 -1
- omlish/formats/dotenv.py +8 -8
- omlish/formats/json/stream/__init__.py +18 -3
- omlish/formats/json/stream/building.py +2 -2
- omlish/formats/json/stream/lexing.py +401 -67
- omlish/formats/json/stream/parsing.py +32 -10
- omlish/formats/json/stream/rendering.py +6 -6
- omlish/formats/json/stream/utils.py +132 -30
- omlish/formats/json5/literals.py +7 -4
- omlish/formats/json5/parsing.py +33 -79
- omlish/formats/json5/stream.py +77 -0
- omlish/formats/logfmt.py +8 -2
- omlish/funcs/genmachine.py +2 -2
- omlish/funcs/guard.py +225 -0
- omlish/graphs/dot/rendering.py +1 -1
- omlish/http/all.py +122 -53
- omlish/http/asgi.py +2 -2
- omlish/http/clients/__init__.py +0 -34
- omlish/http/clients/asyncs.py +153 -0
- omlish/http/clients/base.py +63 -122
- omlish/http/clients/coro/sync.py +171 -0
- omlish/http/clients/default.py +209 -30
- omlish/http/clients/executor.py +56 -0
- omlish/http/clients/httpx.py +78 -13
- omlish/http/clients/middleware.py +181 -0
- omlish/http/clients/sync.py +151 -0
- omlish/http/clients/syncasync.py +49 -0
- omlish/http/clients/urllib.py +8 -5
- omlish/http/coro/client/{client.py → connection.py} +43 -37
- omlish/http/coro/client/headers.py +5 -5
- omlish/http/coro/client/response.py +37 -38
- omlish/http/coro/client/status.py +4 -4
- omlish/http/coro/{client/io.py → io.py} +19 -2
- omlish/http/coro/server/fdio.py +10 -9
- omlish/http/coro/server/server.py +14 -41
- omlish/http/coro/server/sockets.py +7 -6
- omlish/http/flasky/__init__.py +40 -0
- omlish/http/flasky/_compat.py +2 -0
- omlish/http/flasky/api.py +82 -0
- omlish/http/flasky/app.py +203 -0
- omlish/http/flasky/cvs.py +59 -0
- omlish/http/flasky/requests.py +20 -0
- omlish/http/flasky/responses.py +23 -0
- omlish/http/flasky/routes.py +23 -0
- omlish/http/flasky/types.py +15 -0
- omlish/http/handlers.py +3 -2
- omlish/http/headers.py +69 -35
- omlish/http/sse.py +1 -1
- omlish/http/urls.py +67 -0
- omlish/inject/__init__.py +173 -126
- omlish/inject/_dataclasses.py +4986 -0
- omlish/inject/binder.py +10 -49
- omlish/inject/elements.py +27 -0
- omlish/inject/{utils.py → helpers/constfn.py} +3 -3
- omlish/inject/{tags.py → helpers/id.py} +2 -2
- omlish/inject/helpers/multis.py +143 -0
- omlish/inject/helpers/wrappers.py +54 -0
- omlish/inject/impl/elements.py +52 -22
- omlish/inject/impl/injector.py +76 -49
- omlish/inject/impl/inspect.py +11 -1
- omlish/inject/impl/maysync.py +43 -0
- omlish/inject/impl/multis.py +10 -7
- omlish/inject/impl/privates.py +8 -8
- omlish/inject/impl/providers.py +23 -34
- omlish/inject/impl/providersmap.py +43 -0
- omlish/inject/impl/proxy.py +0 -2
- omlish/inject/impl/scopes.py +19 -23
- omlish/inject/impl/sync.py +41 -0
- omlish/inject/injector.py +37 -8
- omlish/inject/inspect.py +35 -0
- omlish/inject/listeners.py +4 -4
- omlish/inject/managed.py +54 -18
- omlish/inject/maysync.py +27 -0
- omlish/inject/multis.py +8 -0
- omlish/inject/overrides.py +3 -3
- omlish/inject/privates.py +6 -0
- omlish/inject/providers.py +8 -1
- omlish/inject/scopes.py +40 -12
- omlish/inject/sync.py +49 -0
- omlish/io/buffers.py +119 -1
- omlish/io/readers.py +29 -0
- omlish/iterators/__init__.py +28 -20
- omlish/iterators/transforms.py +204 -0
- omlish/lang/__init__.py +240 -129
- omlish/lang/_asyncs.cc +186 -0
- omlish/lang/asyncs.py +67 -43
- omlish/lang/{attrs.py → attrstorage.py} +15 -15
- omlish/lang/cached/property.py +2 -2
- omlish/lang/casing.py +11 -0
- omlish/lang/classes/bindable.py +2 -3
- omlish/lang/classes/restrict.py +8 -0
- omlish/lang/classes/simple.py +26 -4
- omlish/lang/collections.py +1 -1
- omlish/lang/contextmanagers.py +59 -9
- omlish/lang/functions.py +31 -33
- omlish/lang/imports/_capture.cc +103 -0
- omlish/lang/imports/capture.py +902 -0
- omlish/lang/imports/lazy.py +0 -25
- omlish/lang/imports/proxy.py +559 -0
- omlish/lang/iterables.py +2 -2
- omlish/lang/lazyglobals.py +49 -14
- omlish/lang/maybes.py +2 -1
- omlish/lang/maysync.py +2 -2
- omlish/lang/params.py +17 -0
- omlish/lang/recursion.py +0 -1
- omlish/lang/resources.py +1 -1
- omlish/lang/sequences.py +124 -0
- omlish/lifecycles/contextmanagers.py +1 -2
- omlish/lifecycles/controller.py +1 -2
- omlish/lite/abstract.py +54 -24
- omlish/lite/asyncs.py +146 -0
- omlish/lite/attrops.py +415 -0
- omlish/lite/cached.py +57 -1
- omlish/lite/contextmanagers.py +4 -4
- omlish/lite/dataclasses.py +55 -0
- omlish/lite/inject.py +5 -4
- omlish/lite/marshal.py +1 -0
- omlish/lite/maybes.py +10 -2
- omlish/lite/maysync.py +22 -5
- omlish/lite/pycharm.py +1 -1
- omlish/lite/strings.py +0 -7
- omlish/lite/timing.py +6 -3
- omlish/lite/typing.py +6 -0
- omlish/logs/_amalg.py +8 -0
- omlish/logs/all.py +59 -31
- omlish/logs/base.py +204 -0
- omlish/logs/contexts.py +171 -0
- omlish/logs/formatters.py +13 -0
- omlish/logs/infos.py +377 -0
- omlish/logs/levels.py +97 -0
- omlish/logs/modules.py +13 -0
- omlish/logs/protocols.py +32 -0
- omlish/logs/standard.py +20 -15
- omlish/logs/std/configs.py +29 -0
- omlish/logs/{filters.py → std/filters.py} +1 -1
- omlish/logs/std/formatters.py +25 -0
- omlish/logs/std/handlers.py +19 -0
- omlish/logs/{json.py → std/json.py} +2 -2
- omlish/logs/std/loggers.py +48 -0
- omlish/logs/{proxy.py → std/proxy.py} +3 -3
- omlish/logs/std/records.py +671 -0
- omlish/logs/typed/bindings.py +108 -37
- omlish/logs/typed/types.py +17 -1
- omlish/logs/typed/values.py +2 -2
- omlish/logs/utils.py +60 -4
- omlish/logs/warnings.py +8 -0
- omlish/manifests/loading.py +8 -1
- omlish/marshal/__init__.py +54 -22
- omlish/marshal/_dataclasses.py +2774 -0
- omlish/marshal/base/configs.py +12 -0
- omlish/marshal/base/contexts.py +36 -21
- omlish/marshal/base/funcs.py +8 -11
- omlish/marshal/base/options.py +8 -0
- omlish/marshal/base/registries.py +146 -44
- omlish/marshal/base/types.py +40 -16
- omlish/marshal/composite/iterables.py +33 -20
- omlish/marshal/composite/literals.py +20 -18
- omlish/marshal/composite/mappings.py +36 -23
- omlish/marshal/composite/maybes.py +29 -19
- omlish/marshal/composite/newtypes.py +16 -16
- omlish/marshal/composite/optionals.py +14 -14
- omlish/marshal/composite/special.py +15 -15
- omlish/marshal/composite/unions/literals.py +93 -0
- omlish/marshal/composite/unions/primitives.py +103 -0
- omlish/marshal/factories/invalidate.py +18 -68
- omlish/marshal/factories/method.py +26 -0
- omlish/marshal/factories/moduleimport/factories.py +22 -65
- omlish/marshal/factories/multi.py +13 -25
- omlish/marshal/factories/recursive.py +42 -56
- omlish/marshal/factories/typecache.py +29 -74
- omlish/marshal/factories/typemap.py +42 -43
- omlish/marshal/objects/dataclasses.py +129 -106
- omlish/marshal/objects/marshal.py +18 -14
- omlish/marshal/objects/namedtuples.py +48 -42
- omlish/marshal/objects/unmarshal.py +19 -15
- omlish/marshal/polymorphism/marshal.py +9 -11
- omlish/marshal/polymorphism/metadata.py +16 -5
- omlish/marshal/polymorphism/standard.py +13 -1
- omlish/marshal/polymorphism/unions.py +15 -105
- omlish/marshal/polymorphism/unmarshal.py +9 -10
- omlish/marshal/singular/enums.py +14 -18
- omlish/marshal/standard.py +10 -6
- omlish/marshal/trivial/any.py +1 -1
- omlish/marshal/trivial/forbidden.py +21 -26
- omlish/math/fixed.py +2 -2
- omlish/metadata.py +23 -1
- omlish/os/atomics.py +2 -2
- omlish/os/forkhooks.py +4 -0
- omlish/os/journald.py +3 -3
- omlish/os/pidfiles/pinning.py +2 -2
- omlish/reflect/ops.py +9 -0
- omlish/reflect/types.py +44 -8
- omlish/secrets/marshal.py +1 -1
- omlish/secrets/secrets.py +6 -3
- omlish/sockets/addresses.py +1 -1
- omlish/sockets/server/handlers.py +2 -2
- omlish/sockets/server/server.py +4 -3
- omlish/sockets/server/ssl.py +2 -2
- omlish/specs/jmespath/__init__.py +12 -3
- omlish/specs/jmespath/_dataclasses.py +2893 -0
- omlish/specs/jmespath/ast.py +1 -1
- omlish/specs/jsonrpc/__init__.py +13 -0
- omlish/specs/jsonrpc/_marshal.py +32 -23
- omlish/specs/jsonrpc/conns.py +10 -7
- omlish/specs/jsonschema/_marshal.py +1 -1
- omlish/specs/jsonschema/keywords/__init__.py +7 -0
- omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
- omlish/specs/openapi/_marshal.py +31 -22
- omlish/sql/__init__.py +15 -20
- omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
- omlish/sql/queries/_marshal.py +3 -3
- omlish/sql/queries/params.py +1 -1
- omlish/sql/queries/rendering.py +1 -1
- omlish/sql/tabledefs/_marshal.py +1 -1
- omlish/subprocesses/all.py +135 -0
- omlish/subprocesses/base.py +8 -3
- omlish/subprocesses/editor.py +1 -1
- omlish/sync.py +181 -20
- omlish/term/alt.py +60 -0
- omlish/term/confirm.py +8 -8
- omlish/term/pager.py +235 -0
- omlish/term/terminfo.py +935 -0
- omlish/term/termstate.py +67 -0
- omlish/term/vt100/terminal.py +0 -3
- omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
- omlish/testing/pytest/plugins/skips.py +2 -5
- omlish/testing/unittest/main.py +3 -3
- omlish/text/docwrap/__init__.py +3 -0
- omlish/text/docwrap/__main__.py +11 -0
- omlish/text/docwrap/api.py +83 -0
- omlish/text/docwrap/cli.py +86 -0
- omlish/text/docwrap/groups.py +84 -0
- omlish/text/docwrap/lists.py +167 -0
- omlish/text/docwrap/parts.py +146 -0
- omlish/text/docwrap/reflowing.py +106 -0
- omlish/text/docwrap/rendering.py +151 -0
- omlish/text/docwrap/utils.py +11 -0
- omlish/text/docwrap/wrapping.py +59 -0
- omlish/text/filecache.py +2 -2
- omlish/text/lorem.py +6 -0
- omlish/text/parts.py +2 -2
- omlish/text/textwrap.py +51 -0
- omlish/typedvalues/__init__.py +1 -1
- omlish/typedvalues/marshal.py +85 -59
- omlish/typedvalues/values.py +2 -1
- {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +36 -38
- {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +323 -385
- omlish/asyncs/bluelet/LICENSE +0 -6
- omlish/asyncs/bluelet/all.py +0 -67
- omlish/asyncs/bluelet/api.py +0 -23
- omlish/asyncs/bluelet/core.py +0 -178
- omlish/asyncs/bluelet/events.py +0 -79
- omlish/asyncs/bluelet/files.py +0 -80
- omlish/asyncs/bluelet/runner.py +0 -417
- omlish/asyncs/bluelet/sockets.py +0 -216
- omlish/asyncs/bridge.py +0 -359
- omlish/asyncs/utils.py +0 -18
- omlish/dataclasses/impl/generation/mangling.py +0 -18
- omlish/defs.py +0 -216
- omlish/dispatch/_dispatch2.py +0 -69
- omlish/dispatch/_dispatch3.py +0 -108
- omlish/dynamic.py +0 -219
- omlish/formats/json/Json.g4 +0 -77
- omlish/formats/json/_antlr/JsonLexer.py +0 -109
- omlish/formats/json/_antlr/JsonListener.py +0 -61
- omlish/formats/json/_antlr/JsonParser.py +0 -457
- omlish/formats/json/_antlr/JsonVisitor.py +0 -42
- omlish/formats/json5/Json5.g4 +0 -168
- omlish/formats/json5/_antlr/Json5Lexer.py +0 -354
- omlish/formats/json5/_antlr/Json5Listener.py +0 -79
- omlish/formats/json5/_antlr/Json5Parser.py +0 -617
- omlish/formats/json5/_antlr/Json5Visitor.py +0 -52
- omlish/funcs/match.py +0 -227
- omlish/io/trampoline.py +0 -289
- omlish/lang/imports/proxyinit.py +0 -665
- omlish/lite/logs.py +0 -4
- omlish/lite/reprs.py +0 -85
- omlish/logs/abc.py +0 -319
- omlish/logs/callers.py +0 -67
- omlish/logs/color.py +0 -27
- omlish/logs/configs.py +0 -29
- omlish/logs/handlers.py +0 -17
- omlish/logs/protocol.py +0 -218
- omlish/logs/timing.py +0 -58
- omlish/marshal/factories/match.py +0 -34
- omlish/marshal/factories/simple.py +0 -28
- omlish/specs/irc/messages/base.py +0 -49
- omlish/specs/irc/messages/formats.py +0 -92
- omlish/specs/irc/messages/messages.py +0 -774
- omlish/specs/irc/messages/parsing.py +0 -98
- omlish/specs/irc/numerics/formats.py +0 -97
- omlish/specs/irc/numerics/numerics.py +0 -865
- omlish/specs/irc/numerics/types.py +0 -59
- omlish/specs/irc/protocol/LICENSE +0 -11
- omlish/specs/irc/protocol/__init__.py +0 -61
- omlish/specs/irc/protocol/consts.py +0 -6
- omlish/specs/irc/protocol/errors.py +0 -30
- omlish/specs/irc/protocol/message.py +0 -21
- omlish/specs/irc/protocol/nuh.py +0 -55
- omlish/specs/irc/protocol/parsing.py +0 -158
- omlish/specs/irc/protocol/rendering.py +0 -153
- omlish/specs/irc/protocol/tags.py +0 -102
- omlish/specs/irc/protocol/utils.py +0 -30
- omlish/specs/proto/Protobuf3.g4 +0 -396
- omlish/specs/proto/__init__.py +0 -0
- omlish/specs/proto/_antlr/Protobuf3Lexer.py +0 -340
- omlish/specs/proto/_antlr/Protobuf3Listener.py +0 -448
- omlish/specs/proto/_antlr/Protobuf3Parser.py +0 -3909
- omlish/specs/proto/_antlr/Protobuf3Visitor.py +0 -257
- omlish/specs/proto/_antlr/__init__.py +0 -0
- omlish/specs/proto/nodes.py +0 -54
- omlish/specs/proto/parsing.py +0 -97
- omlish/sql/parsing/Minisql.g4 +0 -292
- omlish/sql/parsing/__init__.py +0 -0
- omlish/sql/parsing/_antlr/MinisqlLexer.py +0 -322
- omlish/sql/parsing/_antlr/MinisqlListener.py +0 -511
- omlish/sql/parsing/_antlr/MinisqlParser.py +0 -3763
- omlish/sql/parsing/_antlr/MinisqlVisitor.py +0 -292
- omlish/sql/parsing/_antlr/__init__.py +0 -0
- omlish/sql/parsing/parsing.py +0 -119
- omlish/text/antlr/__init__.py +0 -3
- omlish/text/antlr/_runtime/BufferedTokenStream.py +0 -305
- omlish/text/antlr/_runtime/CommonTokenFactory.py +0 -64
- omlish/text/antlr/_runtime/CommonTokenStream.py +0 -90
- omlish/text/antlr/_runtime/FileStream.py +0 -30
- omlish/text/antlr/_runtime/InputStream.py +0 -90
- omlish/text/antlr/_runtime/IntervalSet.py +0 -183
- omlish/text/antlr/_runtime/LICENSE.txt +0 -28
- omlish/text/antlr/_runtime/LL1Analyzer.py +0 -176
- omlish/text/antlr/_runtime/Lexer.py +0 -332
- omlish/text/antlr/_runtime/ListTokenSource.py +0 -147
- omlish/text/antlr/_runtime/Parser.py +0 -583
- omlish/text/antlr/_runtime/ParserInterpreter.py +0 -173
- omlish/text/antlr/_runtime/ParserRuleContext.py +0 -189
- omlish/text/antlr/_runtime/PredictionContext.py +0 -632
- omlish/text/antlr/_runtime/Recognizer.py +0 -150
- omlish/text/antlr/_runtime/RuleContext.py +0 -230
- omlish/text/antlr/_runtime/StdinStream.py +0 -14
- omlish/text/antlr/_runtime/Token.py +0 -158
- omlish/text/antlr/_runtime/TokenStreamRewriter.py +0 -258
- omlish/text/antlr/_runtime/Utils.py +0 -36
- omlish/text/antlr/_runtime/__init__.py +0 -2
- omlish/text/antlr/_runtime/_all.py +0 -24
- omlish/text/antlr/_runtime/_pygrun.py +0 -174
- omlish/text/antlr/_runtime/atn/ATN.py +0 -135
- omlish/text/antlr/_runtime/atn/ATNConfig.py +0 -162
- omlish/text/antlr/_runtime/atn/ATNConfigSet.py +0 -215
- omlish/text/antlr/_runtime/atn/ATNDeserializationOptions.py +0 -27
- omlish/text/antlr/_runtime/atn/ATNDeserializer.py +0 -449
- omlish/text/antlr/_runtime/atn/ATNSimulator.py +0 -50
- omlish/text/antlr/_runtime/atn/ATNState.py +0 -267
- omlish/text/antlr/_runtime/atn/ATNType.py +0 -20
- omlish/text/antlr/_runtime/atn/LexerATNSimulator.py +0 -573
- omlish/text/antlr/_runtime/atn/LexerAction.py +0 -301
- omlish/text/antlr/_runtime/atn/LexerActionExecutor.py +0 -146
- omlish/text/antlr/_runtime/atn/ParserATNSimulator.py +0 -1664
- omlish/text/antlr/_runtime/atn/PredictionMode.py +0 -502
- omlish/text/antlr/_runtime/atn/SemanticContext.py +0 -333
- omlish/text/antlr/_runtime/atn/Transition.py +0 -271
- omlish/text/antlr/_runtime/atn/__init__.py +0 -4
- omlish/text/antlr/_runtime/dfa/DFA.py +0 -136
- omlish/text/antlr/_runtime/dfa/DFASerializer.py +0 -76
- omlish/text/antlr/_runtime/dfa/DFAState.py +0 -129
- omlish/text/antlr/_runtime/dfa/__init__.py +0 -4
- omlish/text/antlr/_runtime/error/DiagnosticErrorListener.py +0 -111
- omlish/text/antlr/_runtime/error/ErrorListener.py +0 -75
- omlish/text/antlr/_runtime/error/ErrorStrategy.py +0 -712
- omlish/text/antlr/_runtime/error/Errors.py +0 -176
- omlish/text/antlr/_runtime/error/__init__.py +0 -4
- omlish/text/antlr/_runtime/tree/Chunk.py +0 -33
- omlish/text/antlr/_runtime/tree/ParseTreeMatch.py +0 -121
- omlish/text/antlr/_runtime/tree/ParseTreePattern.py +0 -75
- omlish/text/antlr/_runtime/tree/ParseTreePatternMatcher.py +0 -377
- omlish/text/antlr/_runtime/tree/RuleTagToken.py +0 -53
- omlish/text/antlr/_runtime/tree/TokenTagToken.py +0 -50
- omlish/text/antlr/_runtime/tree/Tree.py +0 -194
- omlish/text/antlr/_runtime/tree/Trees.py +0 -114
- omlish/text/antlr/_runtime/tree/__init__.py +0 -2
- omlish/text/antlr/_runtime/xpath/XPath.py +0 -278
- omlish/text/antlr/_runtime/xpath/XPathLexer.py +0 -98
- omlish/text/antlr/_runtime/xpath/__init__.py +0 -4
- omlish/text/antlr/delimit.py +0 -109
- omlish/text/antlr/dot.py +0 -41
- omlish/text/antlr/errors.py +0 -14
- omlish/text/antlr/input.py +0 -96
- omlish/text/antlr/parsing.py +0 -54
- omlish/text/antlr/runtime.py +0 -102
- omlish/text/antlr/utils.py +0 -38
- /omlish/{asyncs/bluelet → cexts}/__init__.py +0 -0
- /omlish/{formats/json/_antlr → diag/cmds}/__init__.py +0 -0
- /omlish/{formats/json5/_antlr → http/clients/coro}/__init__.py +0 -0
- /omlish/{specs/irc → inject/helpers}/__init__.py +0 -0
- /omlish/{specs/irc/messages → logs/std}/__init__.py +0 -0
- /omlish/logs/{noisy.py → std/noisy.py} +0 -0
- /omlish/{specs/irc/numerics → marshal/composite/unions}/__init__.py +0 -0
- {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# ruff: noqa: UP043 UP045
|
|
2
|
+
# @omlish-lite
|
|
3
|
+
import dataclasses as dc
|
|
4
|
+
import typing as ta
|
|
5
|
+
|
|
6
|
+
from .asyncs import AsyncHttpClient
|
|
7
|
+
from .asyncs import AsyncStreamHttpResponse
|
|
8
|
+
from .base import HttpClientContext
|
|
9
|
+
from .base import HttpRequest
|
|
10
|
+
from .sync import HttpClient
|
|
11
|
+
from .sync import StreamHttpResponse
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExecutorAsyncHttpClient(AsyncHttpClient):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
run_in_executor: ta.Callable[..., ta.Awaitable],
|
|
21
|
+
client: HttpClient,
|
|
22
|
+
) -> None:
|
|
23
|
+
super().__init__()
|
|
24
|
+
|
|
25
|
+
self._run_in_executor = run_in_executor
|
|
26
|
+
self._client = client
|
|
27
|
+
|
|
28
|
+
@dc.dataclass(frozen=True)
|
|
29
|
+
class _StreamAdapter:
|
|
30
|
+
owner: 'ExecutorAsyncHttpClient'
|
|
31
|
+
resp: StreamHttpResponse
|
|
32
|
+
|
|
33
|
+
async def read1(self, n: int = -1, /) -> bytes:
|
|
34
|
+
return await self.owner._run_in_executor(self.resp.stream.read1, n) # noqa
|
|
35
|
+
|
|
36
|
+
async def read(self, n: int = -1, /) -> bytes:
|
|
37
|
+
return await self.owner._run_in_executor(self.resp.stream.read, n) # noqa
|
|
38
|
+
|
|
39
|
+
async def readall(self) -> bytes:
|
|
40
|
+
return await self.owner._run_in_executor(self.resp.stream.readall) # noqa
|
|
41
|
+
|
|
42
|
+
async def close(self) -> None:
|
|
43
|
+
return await self.owner._run_in_executor(self.resp.close) # noqa
|
|
44
|
+
|
|
45
|
+
async def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> AsyncStreamHttpResponse:
|
|
46
|
+
resp: StreamHttpResponse = await self._run_in_executor(lambda: self._client.stream_request(req, context=ctx))
|
|
47
|
+
return AsyncStreamHttpResponse(
|
|
48
|
+
status=resp.status,
|
|
49
|
+
headers=resp.headers,
|
|
50
|
+
request=req,
|
|
51
|
+
underlying=resp,
|
|
52
|
+
**(dict( # type: ignore
|
|
53
|
+
_stream=(adapter := self._StreamAdapter(self, resp)),
|
|
54
|
+
_closer=adapter.close,
|
|
55
|
+
) if resp.has_data else {}),
|
|
56
|
+
)
|
omlish/http/clients/httpx.py
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- standardize following redirects
|
|
4
|
+
"""
|
|
5
|
+
import contextlib
|
|
1
6
|
import functools
|
|
2
7
|
import typing as ta
|
|
3
8
|
|
|
4
9
|
from ... import dataclasses as dc
|
|
5
10
|
from ... import lang
|
|
11
|
+
from ...io.buffers import ReadableListBuffer
|
|
6
12
|
from ..headers import HttpHeaders
|
|
7
|
-
from .
|
|
13
|
+
from .asyncs import AsyncHttpClient
|
|
14
|
+
from .asyncs import AsyncStreamHttpResponse
|
|
15
|
+
from .base import HttpClientContext
|
|
8
16
|
from .base import HttpClientError
|
|
9
17
|
from .base import HttpRequest
|
|
10
|
-
from .
|
|
18
|
+
from .sync import HttpClient
|
|
19
|
+
from .sync import StreamHttpResponse
|
|
11
20
|
|
|
12
21
|
|
|
13
22
|
if ta.TYPE_CHECKING:
|
|
@@ -24,16 +33,13 @@ class HttpxHttpClient(HttpClient):
|
|
|
24
33
|
class _StreamAdapter:
|
|
25
34
|
it: ta.Iterator[bytes]
|
|
26
35
|
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
return
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return next(self.it)
|
|
33
|
-
except StopIteration:
|
|
34
|
-
return b''
|
|
36
|
+
def read1(self, n: int = -1, /) -> bytes:
|
|
37
|
+
try:
|
|
38
|
+
return next(self.it)
|
|
39
|
+
except StopIteration:
|
|
40
|
+
return b''
|
|
35
41
|
|
|
36
|
-
def _stream_request(self, req: HttpRequest) -> StreamHttpResponse:
|
|
42
|
+
def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> StreamHttpResponse:
|
|
37
43
|
try:
|
|
38
44
|
resp_cm = httpx.stream(
|
|
39
45
|
method=req.method_or_default,
|
|
@@ -55,7 +61,7 @@ class HttpxHttpClient(HttpClient):
|
|
|
55
61
|
headers=HttpHeaders(resp.headers.raw),
|
|
56
62
|
request=req,
|
|
57
63
|
underlying=resp,
|
|
58
|
-
|
|
64
|
+
_stream=ReadableListBuffer().new_buffered_reader(self._StreamAdapter(resp.iter_bytes())),
|
|
59
65
|
_closer=resp_close, # type: ignore
|
|
60
66
|
)
|
|
61
67
|
|
|
@@ -63,6 +69,65 @@ class HttpxHttpClient(HttpClient):
|
|
|
63
69
|
resp_close()
|
|
64
70
|
raise HttpClientError from e
|
|
65
71
|
|
|
66
|
-
except
|
|
72
|
+
except BaseException:
|
|
67
73
|
resp_close()
|
|
68
74
|
raise
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
##
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class HttpxAsyncHttpClient(AsyncHttpClient):
|
|
81
|
+
@dc.dataclass(frozen=True)
|
|
82
|
+
class _StreamAdapter:
|
|
83
|
+
it: ta.AsyncIterator[bytes]
|
|
84
|
+
|
|
85
|
+
async def read1(self, n: int = -1, /) -> bytes:
|
|
86
|
+
try:
|
|
87
|
+
return await anext(self.it)
|
|
88
|
+
except StopAsyncIteration:
|
|
89
|
+
return b''
|
|
90
|
+
|
|
91
|
+
async def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> AsyncStreamHttpResponse:
|
|
92
|
+
es = contextlib.AsyncExitStack()
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
client = await es.enter_async_context(httpx.AsyncClient())
|
|
96
|
+
|
|
97
|
+
resp = await es.enter_async_context(client.stream(
|
|
98
|
+
method=req.method_or_default,
|
|
99
|
+
url=req.url,
|
|
100
|
+
headers=req.headers_ or None, # type: ignore
|
|
101
|
+
content=req.data,
|
|
102
|
+
timeout=req.timeout_s,
|
|
103
|
+
))
|
|
104
|
+
|
|
105
|
+
it = resp.aiter_bytes()
|
|
106
|
+
|
|
107
|
+
# FIXME:
|
|
108
|
+
# this has a tendency to raise `RuntimeError: async generator ignored GeneratorExit` when all of the
|
|
109
|
+
# following conditions are met:
|
|
110
|
+
# - stopped iterating midway through
|
|
111
|
+
# - shutting down the event loop
|
|
112
|
+
# - debugging under pycharm / pydevd
|
|
113
|
+
# - running under asyncio
|
|
114
|
+
# it does not seem to happen unless all of these conditions are met. see:
|
|
115
|
+
# https://gist.github.com/wrmsr/a0578ee5d5371b53804cfb56aeb84cdf .
|
|
116
|
+
es.push_async_callback(it.aclose) # type: ignore[attr-defined]
|
|
117
|
+
|
|
118
|
+
return AsyncStreamHttpResponse(
|
|
119
|
+
status=resp.status_code,
|
|
120
|
+
headers=HttpHeaders(resp.headers.raw),
|
|
121
|
+
request=req,
|
|
122
|
+
underlying=resp,
|
|
123
|
+
_stream=ReadableListBuffer().new_async_buffered_reader(self._StreamAdapter(it)),
|
|
124
|
+
_closer=es.aclose,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
except httpx.HTTPError as e:
|
|
128
|
+
await es.aclose()
|
|
129
|
+
raise HttpClientError from e
|
|
130
|
+
|
|
131
|
+
except BaseException:
|
|
132
|
+
await es.aclose()
|
|
133
|
+
raise
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# ruff: noqa: UP007 UP043 UP045
|
|
2
|
+
# @omlish-lite
|
|
3
|
+
"""
|
|
4
|
+
TODO:
|
|
5
|
+
- redirect
|
|
6
|
+
- referrer header?
|
|
7
|
+
- non-forwarded headers, host check, etc lol
|
|
8
|
+
- 'check' kw becomes StatusCheckingMiddleware?
|
|
9
|
+
"""
|
|
10
|
+
import dataclasses as dc
|
|
11
|
+
import typing as ta
|
|
12
|
+
import urllib.parse
|
|
13
|
+
|
|
14
|
+
from ...lite.abstract import Abstract
|
|
15
|
+
from ...lite.check import check
|
|
16
|
+
from ..urls import parsed_url_replace
|
|
17
|
+
from .asyncs import AsyncHttpClient
|
|
18
|
+
from .asyncs import AsyncStreamHttpResponse
|
|
19
|
+
from .base import BaseHttpClient
|
|
20
|
+
from .base import BaseHttpResponse
|
|
21
|
+
from .base import HttpClientContext
|
|
22
|
+
from .base import HttpClientError
|
|
23
|
+
from .base import HttpRequest
|
|
24
|
+
from .sync import HttpClient
|
|
25
|
+
from .sync import StreamHttpResponse
|
|
26
|
+
from .sync import close_http_client_response
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
BaseHttpClientT = ta.TypeVar('BaseHttpClientT', bound=BaseHttpClient)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class HttpClientMiddleware(Abstract):
|
|
36
|
+
def process_request(
|
|
37
|
+
self,
|
|
38
|
+
ctx: HttpClientContext,
|
|
39
|
+
req: HttpRequest,
|
|
40
|
+
) -> HttpRequest:
|
|
41
|
+
return req
|
|
42
|
+
|
|
43
|
+
def process_response(
|
|
44
|
+
self,
|
|
45
|
+
ctx: HttpClientContext,
|
|
46
|
+
req: HttpRequest,
|
|
47
|
+
resp: BaseHttpResponse,
|
|
48
|
+
) -> ta.Union[BaseHttpResponse, HttpRequest]:
|
|
49
|
+
return resp
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AbstractMiddlewareHttpClient(Abstract, ta.Generic[BaseHttpClientT]):
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
client: BaseHttpClientT,
|
|
56
|
+
middlewares: ta.Iterable[HttpClientMiddleware],
|
|
57
|
+
) -> None:
|
|
58
|
+
super().__init__()
|
|
59
|
+
|
|
60
|
+
self._client = client
|
|
61
|
+
self._middlewares = list(middlewares)
|
|
62
|
+
|
|
63
|
+
def _process_request(
|
|
64
|
+
self,
|
|
65
|
+
ctx: HttpClientContext,
|
|
66
|
+
req: HttpRequest,
|
|
67
|
+
) -> HttpRequest:
|
|
68
|
+
for mw in self._middlewares:
|
|
69
|
+
req = mw.process_request(ctx, req)
|
|
70
|
+
return req
|
|
71
|
+
|
|
72
|
+
def _process_response(
|
|
73
|
+
self,
|
|
74
|
+
ctx: HttpClientContext,
|
|
75
|
+
req: HttpRequest,
|
|
76
|
+
resp: BaseHttpResponse,
|
|
77
|
+
) -> ta.Union[BaseHttpResponse, HttpRequest]:
|
|
78
|
+
for mw in self._middlewares:
|
|
79
|
+
nxt = mw.process_response(ctx, req, resp)
|
|
80
|
+
if isinstance(nxt, HttpRequest):
|
|
81
|
+
return nxt
|
|
82
|
+
else:
|
|
83
|
+
resp = nxt
|
|
84
|
+
return resp
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
#
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class MiddlewareHttpClient(AbstractMiddlewareHttpClient[HttpClient], HttpClient):
|
|
91
|
+
def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> StreamHttpResponse:
|
|
92
|
+
while True:
|
|
93
|
+
req = self._process_request(ctx, req)
|
|
94
|
+
|
|
95
|
+
resp = self._client.stream_request(req, context=ctx)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
out = self._process_response(ctx, req, resp)
|
|
99
|
+
|
|
100
|
+
if isinstance(out, HttpRequest):
|
|
101
|
+
close_http_client_response(resp)
|
|
102
|
+
req = out
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
elif isinstance(out, StreamHttpResponse):
|
|
106
|
+
return out
|
|
107
|
+
|
|
108
|
+
else:
|
|
109
|
+
raise TypeError(out) # noqa
|
|
110
|
+
|
|
111
|
+
except Exception:
|
|
112
|
+
close_http_client_response(resp)
|
|
113
|
+
raise
|
|
114
|
+
|
|
115
|
+
raise RuntimeError
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class MiddlewareAsyncHttpClient(AbstractMiddlewareHttpClient[AsyncHttpClient], AsyncHttpClient):
|
|
119
|
+
def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> ta.Awaitable[AsyncStreamHttpResponse]:
|
|
120
|
+
return self._client.stream_request(self._process_request(ctx, req))
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
##
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TooManyRedirectsHttpClientError(HttpClientError):
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class RedirectHandlingHttpClientMiddleware(HttpClientMiddleware):
|
|
131
|
+
DEFAULT_MAX_REDIRECTS: ta.ClassVar[int] = 5
|
|
132
|
+
|
|
133
|
+
def __init__(
|
|
134
|
+
self,
|
|
135
|
+
*,
|
|
136
|
+
max_redirects: ta.Optional[int] = None,
|
|
137
|
+
) -> None:
|
|
138
|
+
super().__init__()
|
|
139
|
+
|
|
140
|
+
if max_redirects is None:
|
|
141
|
+
max_redirects = self.DEFAULT_MAX_REDIRECTS
|
|
142
|
+
self._max_redirects = max_redirects
|
|
143
|
+
|
|
144
|
+
@dc.dataclass()
|
|
145
|
+
class _State:
|
|
146
|
+
num_redirects: int = 0
|
|
147
|
+
|
|
148
|
+
def _get_state(self, ctx: HttpClientContext) -> _State:
|
|
149
|
+
try:
|
|
150
|
+
return ctx._dct[self._State] # noqa
|
|
151
|
+
except KeyError:
|
|
152
|
+
ret = ctx._dct[self._State] = self._State() # noqa
|
|
153
|
+
return ret
|
|
154
|
+
|
|
155
|
+
def process_response(
|
|
156
|
+
self,
|
|
157
|
+
ctx: HttpClientContext,
|
|
158
|
+
req: HttpRequest,
|
|
159
|
+
resp: BaseHttpResponse,
|
|
160
|
+
) -> ta.Union[BaseHttpResponse, HttpRequest]: # noqa
|
|
161
|
+
if resp.status == 302:
|
|
162
|
+
st = self._get_state(ctx)
|
|
163
|
+
if st.num_redirects >= self._max_redirects:
|
|
164
|
+
raise TooManyRedirectsHttpClientError
|
|
165
|
+
st.num_redirects += 1
|
|
166
|
+
|
|
167
|
+
rd_url = check.not_none(resp.headers).single_str_dct['location']
|
|
168
|
+
|
|
169
|
+
rd_purl = urllib.parse.urlparse(rd_url)
|
|
170
|
+
if not rd_purl.netloc:
|
|
171
|
+
rq_purl = urllib.parse.urlparse(req.url)
|
|
172
|
+
rd_purl = parsed_url_replace(
|
|
173
|
+
rd_purl,
|
|
174
|
+
scheme=rq_purl.scheme,
|
|
175
|
+
netloc=rq_purl.netloc,
|
|
176
|
+
)
|
|
177
|
+
rd_url = urllib.parse.urlunparse(rd_purl)
|
|
178
|
+
|
|
179
|
+
return dc.replace(req, url=rd_url)
|
|
180
|
+
|
|
181
|
+
return resp
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# ruff: noqa: UP045
|
|
2
|
+
# @omlish-lite
|
|
3
|
+
import abc
|
|
4
|
+
import contextlib
|
|
5
|
+
import dataclasses as dc
|
|
6
|
+
import typing as ta
|
|
7
|
+
|
|
8
|
+
from ...io.readers import BufferedBytesReader
|
|
9
|
+
from ...lite.abstract import Abstract
|
|
10
|
+
from ...lite.dataclasses import dataclass_shallow_asdict
|
|
11
|
+
from .base import BaseHttpClient
|
|
12
|
+
from .base import BaseHttpResponse
|
|
13
|
+
from .base import BaseHttpResponseT
|
|
14
|
+
from .base import HttpClientContext
|
|
15
|
+
from .base import HttpRequest
|
|
16
|
+
from .base import HttpResponse
|
|
17
|
+
from .base import HttpStatusError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
StreamHttpResponseT = ta.TypeVar('StreamHttpResponseT', bound='StreamHttpResponse')
|
|
21
|
+
HttpClientT = ta.TypeVar('HttpClientT', bound='HttpClient')
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@ta.final
|
|
28
|
+
@dc.dataclass(frozen=True) # kw_only=True
|
|
29
|
+
class StreamHttpResponse(BaseHttpResponse):
|
|
30
|
+
_stream: ta.Optional[BufferedBytesReader] = None
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def stream(self) -> 'BufferedBytesReader':
|
|
34
|
+
if (st := self._stream) is None:
|
|
35
|
+
raise TypeError('No data')
|
|
36
|
+
return st
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def has_data(self) -> bool:
|
|
40
|
+
return self._stream is not None
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
|
|
44
|
+
_closer: ta.Optional[ta.Callable[[], None]] = None
|
|
45
|
+
|
|
46
|
+
def __enter__(self: StreamHttpResponseT) -> StreamHttpResponseT:
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
50
|
+
self.close()
|
|
51
|
+
|
|
52
|
+
def close(self) -> None:
|
|
53
|
+
if (c := self._closer) is not None:
|
|
54
|
+
c() # noqa
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
#
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def close_http_client_response(resp: BaseHttpResponse) -> None:
|
|
61
|
+
if isinstance(resp, HttpResponse):
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
elif isinstance(resp, StreamHttpResponse):
|
|
65
|
+
resp.close()
|
|
66
|
+
|
|
67
|
+
else:
|
|
68
|
+
raise TypeError(resp)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@contextlib.contextmanager
|
|
72
|
+
def closing_http_client_response(resp: BaseHttpResponseT) -> ta.Iterator[BaseHttpResponseT]:
|
|
73
|
+
if isinstance(resp, HttpResponse):
|
|
74
|
+
yield resp
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
elif isinstance(resp, StreamHttpResponse):
|
|
78
|
+
with contextlib.closing(resp):
|
|
79
|
+
yield resp
|
|
80
|
+
|
|
81
|
+
else:
|
|
82
|
+
raise TypeError(resp)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def read_http_client_response(resp: BaseHttpResponse) -> HttpResponse:
|
|
86
|
+
if isinstance(resp, HttpResponse):
|
|
87
|
+
return resp
|
|
88
|
+
|
|
89
|
+
elif isinstance(resp, StreamHttpResponse):
|
|
90
|
+
return HttpResponse(**{
|
|
91
|
+
**{k: v for k, v in dataclass_shallow_asdict(resp).items() if k not in ('_stream', '_closer')},
|
|
92
|
+
**({'data': resp.stream.readall()} if resp.has_data else {}),
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
else:
|
|
96
|
+
raise TypeError(resp)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class HttpClient(BaseHttpClient, Abstract):
|
|
103
|
+
def __enter__(self: HttpClientT) -> HttpClientT:
|
|
104
|
+
return self
|
|
105
|
+
|
|
106
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
def request(
|
|
110
|
+
self,
|
|
111
|
+
req: HttpRequest,
|
|
112
|
+
*,
|
|
113
|
+
context: ta.Optional[HttpClientContext] = None,
|
|
114
|
+
check: bool = False,
|
|
115
|
+
) -> HttpResponse:
|
|
116
|
+
with closing_http_client_response(self.stream_request(
|
|
117
|
+
req,
|
|
118
|
+
context=context,
|
|
119
|
+
check=check,
|
|
120
|
+
)) as resp:
|
|
121
|
+
return read_http_client_response(resp)
|
|
122
|
+
|
|
123
|
+
def stream_request(
|
|
124
|
+
self,
|
|
125
|
+
req: HttpRequest,
|
|
126
|
+
*,
|
|
127
|
+
context: ta.Optional[HttpClientContext] = None,
|
|
128
|
+
check: bool = False,
|
|
129
|
+
) -> StreamHttpResponse:
|
|
130
|
+
if context is None:
|
|
131
|
+
context = HttpClientContext()
|
|
132
|
+
|
|
133
|
+
resp = self._stream_request(context, req)
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
if check and not resp.is_success:
|
|
137
|
+
if isinstance(resp.underlying, Exception):
|
|
138
|
+
cause = resp.underlying
|
|
139
|
+
else:
|
|
140
|
+
cause = None
|
|
141
|
+
raise HttpStatusError(read_http_client_response(resp)) from cause # noqa
|
|
142
|
+
|
|
143
|
+
except Exception:
|
|
144
|
+
close_http_client_response(resp)
|
|
145
|
+
raise
|
|
146
|
+
|
|
147
|
+
return resp
|
|
148
|
+
|
|
149
|
+
@abc.abstractmethod
|
|
150
|
+
def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> StreamHttpResponse:
|
|
151
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# ruff: noqa: UP043 UP045
|
|
2
|
+
# @omlish-lite
|
|
3
|
+
import dataclasses as dc
|
|
4
|
+
|
|
5
|
+
from .asyncs import AsyncHttpClient
|
|
6
|
+
from .asyncs import AsyncStreamHttpResponse
|
|
7
|
+
from .base import HttpClientContext
|
|
8
|
+
from .base import HttpRequest
|
|
9
|
+
from .sync import HttpClient
|
|
10
|
+
from .sync import StreamHttpResponse
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SyncAsyncHttpClient(AsyncHttpClient):
|
|
17
|
+
def __init__(self, client: HttpClient) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
|
|
20
|
+
self._client = client
|
|
21
|
+
|
|
22
|
+
@dc.dataclass(frozen=True)
|
|
23
|
+
class _StreamAdapter:
|
|
24
|
+
ul: StreamHttpResponse
|
|
25
|
+
|
|
26
|
+
async def read1(self, n: int = -1, /) -> bytes:
|
|
27
|
+
return self.ul.stream.read1(n)
|
|
28
|
+
|
|
29
|
+
async def read(self, n: int = -1, /) -> bytes:
|
|
30
|
+
return self.ul.stream.read(n)
|
|
31
|
+
|
|
32
|
+
async def readall(self) -> bytes:
|
|
33
|
+
return self.ul.stream.readall()
|
|
34
|
+
|
|
35
|
+
async def close(self) -> None:
|
|
36
|
+
self.ul.close()
|
|
37
|
+
|
|
38
|
+
async def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> AsyncStreamHttpResponse:
|
|
39
|
+
resp = self._client.stream_request(req, context=ctx)
|
|
40
|
+
return AsyncStreamHttpResponse(
|
|
41
|
+
status=resp.status,
|
|
42
|
+
headers=resp.headers,
|
|
43
|
+
request=req,
|
|
44
|
+
underlying=resp,
|
|
45
|
+
**(dict( # type: ignore
|
|
46
|
+
_stream=(adapter := self._StreamAdapter(resp)),
|
|
47
|
+
_closer=adapter.close,
|
|
48
|
+
) if resp.has_data else {}),
|
|
49
|
+
)
|
omlish/http/clients/urllib.py
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
# ruff: noqa: UP043 UP045
|
|
2
|
+
# @omlish-lite
|
|
1
3
|
import http.client
|
|
2
4
|
import typing as ta
|
|
3
5
|
import urllib.error
|
|
4
6
|
import urllib.request
|
|
5
7
|
|
|
8
|
+
from ...io.buffers import ReadableListBuffer
|
|
6
9
|
from ..headers import HttpHeaders
|
|
7
10
|
from .base import DEFAULT_ENCODING
|
|
8
|
-
from .base import
|
|
11
|
+
from .base import HttpClientContext
|
|
9
12
|
from .base import HttpClientError
|
|
10
13
|
from .base import HttpRequest
|
|
11
|
-
from .
|
|
14
|
+
from .sync import HttpClient
|
|
15
|
+
from .sync import StreamHttpResponse
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
##
|
|
@@ -39,7 +43,7 @@ class UrllibHttpClient(HttpClient):
|
|
|
39
43
|
data=d,
|
|
40
44
|
)
|
|
41
45
|
|
|
42
|
-
def _stream_request(self, req: HttpRequest) -> StreamHttpResponse:
|
|
46
|
+
def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> StreamHttpResponse:
|
|
43
47
|
try:
|
|
44
48
|
resp = urllib.request.urlopen( # noqa
|
|
45
49
|
self._build_request(req),
|
|
@@ -53,7 +57,6 @@ class UrllibHttpClient(HttpClient):
|
|
|
53
57
|
headers=HttpHeaders(e.headers.items()),
|
|
54
58
|
request=req,
|
|
55
59
|
underlying=e,
|
|
56
|
-
stream=e, # noqa
|
|
57
60
|
_closer=e.close,
|
|
58
61
|
)
|
|
59
62
|
|
|
@@ -70,7 +73,7 @@ class UrllibHttpClient(HttpClient):
|
|
|
70
73
|
headers=HttpHeaders(resp.headers.items()),
|
|
71
74
|
request=req,
|
|
72
75
|
underlying=resp,
|
|
73
|
-
|
|
76
|
+
_stream=ReadableListBuffer().new_buffered_reader(resp),
|
|
74
77
|
_closer=resp.close,
|
|
75
78
|
)
|
|
76
79
|
|