httk-core 2.0.0__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.
Files changed (49) hide show
  1. httk/core/__init__.py +112 -0
  2. httk/core/_discover.py +35 -0
  3. httk/core/_plugins.py +95 -0
  4. httk/core/dataloader.py +263 -0
  5. httk/core/datastream/__init__.py +80 -0
  6. httk/core/datastream/bytestream_api.py +33 -0
  7. httk/core/datastream/bytestream_backend.py +12 -0
  8. httk/core/datastream/bytestream_bytes.py +52 -0
  9. httk/core/datastream/bytestream_bytes_view.py +31 -0
  10. httk/core/datastream/bytestream_common.py +38 -0
  11. httk/core/datastream/bytestream_file.py +53 -0
  12. httk/core/datastream/bytestream_file_view.py +189 -0
  13. httk/core/datastream/bytestream_filename.py +53 -0
  14. httk/core/datastream/bytestream_filename_view.py +34 -0
  15. httk/core/datastream/bytestream_like.py +17 -0
  16. httk/core/datastream/bytestream_request.py +69 -0
  17. httk/core/datastream/bytestream_request_view.py +44 -0
  18. httk/core/datastream/bytestream_url.py +74 -0
  19. httk/core/datastream/bytestream_url_view.py +35 -0
  20. httk/core/datastream/bytestream_view.py +16 -0
  21. httk/core/datastream/compression.py +183 -0
  22. httk/core/datastream/textstream_api.py +33 -0
  23. httk/core/datastream/textstream_backend.py +12 -0
  24. httk/core/datastream/textstream_common.py +37 -0
  25. httk/core/datastream/textstream_file.py +45 -0
  26. httk/core/datastream/textstream_file_view.py +201 -0
  27. httk/core/datastream/textstream_filename.py +55 -0
  28. httk/core/datastream/textstream_filename_view.py +35 -0
  29. httk/core/datastream/textstream_like.py +15 -0
  30. httk/core/datastream/textstream_request.py +72 -0
  31. httk/core/datastream/textstream_request_view.py +44 -0
  32. httk/core/datastream/textstream_string.py +47 -0
  33. httk/core/datastream/textstream_string_view.py +31 -0
  34. httk/core/datastream/textstream_url.py +77 -0
  35. httk/core/datastream/textstream_url_view.py +35 -0
  36. httk/core/datastream/textstream_view.py +16 -0
  37. httk/core/loading.py +29 -0
  38. httk/core/py.typed +0 -0
  39. httk/core/register.py +30 -0
  40. httk/core/views/__init__.py +5 -0
  41. httk/core/views/backend.py +49 -0
  42. httk/core/views/unwrapping.py +12 -0
  43. httk/core/views/view.py +56 -0
  44. httk/handlers/core/__init__.py +0 -0
  45. httk_core-2.0.0.dist-info/METADATA +76 -0
  46. httk_core-2.0.0.dist-info/RECORD +49 -0
  47. httk_core-2.0.0.dist-info/WHEEL +5 -0
  48. httk_core-2.0.0.dist-info/licenses/LICENSE +661 -0
  49. httk_core-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,44 @@
1
+ import urllib.request
2
+ from typing import Any, Self
3
+
4
+ from ..views import unwrap
5
+ from .textstream_backend import TextstreamBackend
6
+ from .textstream_like import TextstreamLike
7
+ from .textstream_view import TextstreamView
8
+
9
+
10
+ class TextstreamRequestView(TextstreamView, urllib.request.Request):
11
+ """
12
+ A view presenting an underlying data streaming backend via a urllib.request.Request.
13
+ This view is mostly useful for providing a Request to functions that will open it.
14
+ Note: this view is not lazy (it does not fetch); it only mirrors the underlying request/URL.
15
+
16
+ Raises TypeError if created with a streaming data source that does not come with a URL.
17
+ """
18
+
19
+ _backend: TextstreamBackend
20
+
21
+ def __new__(cls, obj: TextstreamLike, **hints: Any) -> Self:
22
+ if isinstance(obj, cls):
23
+ return obj
24
+ backend = cls._prepare_backend(obj, hints)
25
+ request = getattr(backend, "request", None)
26
+ if request is not None:
27
+ full_url, data, headers = request.full_url, request.data, request.headers
28
+ else:
29
+ url = getattr(backend, "url", None)
30
+ if url is None:
31
+ raise TypeError("This backend cannot be represented as a Request (no underlying URL)")
32
+ full_url, data, headers = url, None, {}
33
+ instance = super().__new__(cls)
34
+ # Request is mutable, so its state is initialized here in __new__ (keeping __init__ a no-op),
35
+ # so that rewrapping an existing view via cls(view) does not re-initialize it.
36
+ urllib.request.Request.__init__(instance, full_url, data=data, headers=headers)
37
+ instance._backend = backend
38
+ return instance
39
+
40
+ def __init__(self, obj: TextstreamLike, **hints: Any) -> None:
41
+ pass
42
+
43
+ def unwrap(self) -> Any:
44
+ return unwrap(self._backend)
@@ -0,0 +1,47 @@
1
+ import io
2
+ from typing import Any
3
+
4
+ from .compression import reject_text_native_compression
5
+ from .textstream_backend import TextstreamBackend
6
+ from .textstream_common import TextstreamCommon
7
+
8
+
9
+ class TextstreamString(TextstreamCommon, TextstreamBackend):
10
+ """
11
+ Backend for streaming text backed by an actual string
12
+ """
13
+
14
+ s: str
15
+ _f: io.TextIOBase | None
16
+ _underlying: io.IOBase | None
17
+ _closed: bool
18
+
19
+ # Cannot type annotate __new__ as `Self | None` for some reason
20
+ def __new__(cls, content: str, **hints: Any) -> Any:
21
+ if not isinstance(content, str):
22
+ return None
23
+ if hints and hints.get("kind", "content") != "content":
24
+ return None
25
+ return super().__new__(cls)
26
+
27
+ def __init__(self, content: str, **hints: Any) -> None:
28
+ reject_text_native_compression(hints.get("compression"))
29
+ self.s = content
30
+ self._f = None
31
+ self._underlying = None
32
+ self._closed = False
33
+
34
+ def _ensure_f(self) -> io.TextIOBase:
35
+ if self._closed:
36
+ raise ValueError("I/O operation on closed stream")
37
+ if self._f is None:
38
+ self._f = io.StringIO(self.s)
39
+ return self._f
40
+
41
+ @property
42
+ def name(self) -> str | None:
43
+ return None
44
+
45
+ @property
46
+ def closed(self) -> bool:
47
+ return self._closed
@@ -0,0 +1,31 @@
1
+ from typing import Any, Self
2
+
3
+ from ..views import unwrap
4
+ from .textstream_backend import TextstreamBackend
5
+ from .textstream_like import TextstreamLike
6
+ from .textstream_view import TextstreamView
7
+
8
+
9
+ class TextstreamStringView(TextstreamView, str):
10
+ """
11
+ A view presenting an underlying data streaming as a string.
12
+ This view can be used both to pass a string in place of streaming data, and for reading streaming data into a string.
13
+ Note: this view is not lazy (this is impossible for views inherting str, since str is immutable), hence all the streaming data
14
+ is read immedately upon creating this view.
15
+ """
16
+
17
+ _backend: TextstreamBackend
18
+
19
+ def __new__(cls, obj: TextstreamLike, **hints: Any) -> Self:
20
+ if isinstance(obj, cls):
21
+ return obj
22
+ backend = cls._prepare_backend(obj, hints)
23
+ instance = super().__new__(cls, backend.read())
24
+ instance._backend = backend
25
+ return instance
26
+
27
+ def __init__(self, obj: TextstreamLike, **hints: Any) -> None:
28
+ super().__init__()
29
+
30
+ def unwrap(self) -> Any:
31
+ return unwrap(self._backend)
@@ -0,0 +1,77 @@
1
+ import io
2
+ import urllib.parse
3
+ import urllib.request
4
+ from typing import Any, cast
5
+
6
+ from .compression import open_compressed, validate_compression
7
+ from .textstream_backend import TextstreamBackend
8
+ from .textstream_common import TextstreamCommon
9
+
10
+ _URL_SCHEMES = ("http", "https", "ftp", "file")
11
+
12
+
13
+ class TextstreamURL(TextstreamCommon, TextstreamBackend):
14
+ """
15
+ Backend for streaming text fetched from a URL string.
16
+ A bare string is interpreted as a URL when its scheme is one of http, https, ftp, or file,
17
+ or when an explicit kind="url" hint is given.
18
+ """
19
+
20
+ _url: str
21
+ _timeout: float | None
22
+ _encoding: str | None
23
+ _compression: str
24
+ _f: io.TextIOBase | None
25
+ _underlying: io.IOBase | None
26
+ _closed: bool
27
+
28
+ # mypy does not allow to type annotate __new__ as `Self | None` for some reason
29
+ def __new__(cls, url: str, **hints: Any) -> Any:
30
+ if not isinstance(url, str):
31
+ return None
32
+ kind = hints.get("kind")
33
+ if kind == "url":
34
+ if not urllib.parse.urlsplit(url).scheme:
35
+ return None
36
+ return super().__new__(cls)
37
+ if kind is None and urllib.parse.urlsplit(url).scheme in _URL_SCHEMES:
38
+ return super().__new__(cls)
39
+ return None
40
+
41
+ def __init__(self, url: str, **hints: Any) -> None:
42
+ self._url = url
43
+ self._timeout = hints.get("timeout")
44
+ self._encoding = hints.get("encoding")
45
+ self._compression = hints.get("compression", "auto")
46
+ validate_compression(self._compression)
47
+ self._f = None
48
+ self._underlying = None
49
+ self._closed = False
50
+
51
+ def _ensure_f(self) -> io.TextIOBase:
52
+ if self._closed:
53
+ raise ValueError("I/O operation on closed stream")
54
+ if self._f is None:
55
+ if self._timeout is None:
56
+ resp = urllib.request.urlopen(self._url)
57
+ else:
58
+ resp = urllib.request.urlopen(self._url, timeout=self._timeout)
59
+ encoding = self._encoding or resp.headers.get_content_charset() or "utf-8"
60
+ raw = cast(io.IOBase, resp)
61
+ name = urllib.parse.urlsplit(self._url).path
62
+ decompressed = open_compressed(raw, compression=self._compression, name=name)
63
+ self._underlying = raw if decompressed is not raw else None
64
+ self._f = io.TextIOWrapper(cast(io.BufferedReader, decompressed), encoding=encoding)
65
+ return self._f
66
+
67
+ @property
68
+ def name(self) -> str | None:
69
+ return None
70
+
71
+ @property
72
+ def url(self) -> str:
73
+ return self._url
74
+
75
+ @property
76
+ def closed(self) -> bool:
77
+ return self._closed
@@ -0,0 +1,35 @@
1
+ from typing import Any, Self
2
+
3
+ from ..views import unwrap
4
+ from .textstream_backend import TextstreamBackend
5
+ from .textstream_like import TextstreamLike
6
+ from .textstream_view import TextstreamView
7
+
8
+
9
+ class TextstreamURLView(TextstreamView, str):
10
+ """
11
+ A view presenting an underlying data streaming backend via a URL string.
12
+ This view is mostly useful for providing a URL to functions that will open it.
13
+ Note: this view is not lazy (this is impossible for views inheriting str, since str is immutable).
14
+
15
+ Raises TypeError if created with a streaming data source that does not come with a URL.
16
+ """
17
+
18
+ _backend: TextstreamBackend
19
+
20
+ def __new__(cls, obj: TextstreamLike, **hints: Any) -> Self:
21
+ if isinstance(obj, cls):
22
+ return obj
23
+ backend = cls._prepare_backend(obj, hints)
24
+ url = getattr(backend, "url", None)
25
+ if url is None:
26
+ raise TypeError("This backend cannot be represented as a URL (no underlying URL)")
27
+ instance = super().__new__(cls, url)
28
+ instance._backend = backend
29
+ return instance
30
+
31
+ def __init__(self, obj: TextstreamLike, **hints: Any) -> None:
32
+ super().__init__()
33
+
34
+ def unwrap(self) -> Any:
35
+ return unwrap(self._backend)
@@ -0,0 +1,16 @@
1
+ from typing import ClassVar, Self
2
+
3
+ from ..views import View
4
+ from .textstream_backend import TextstreamBackend
5
+
6
+
7
+ class TextstreamView(View[TextstreamBackend]):
8
+ """
9
+ Abstract base class for all views of streaming text data.
10
+ """
11
+
12
+ _backend_base_cls: ClassVar[type[TextstreamBackend]] = TextstreamBackend # type: ignore[type-abstract]
13
+ _view_base_cls: ClassVar[type[Self]]
14
+
15
+
16
+ TextstreamView._view_base_cls = TextstreamView
httk/core/loading.py ADDED
@@ -0,0 +1,29 @@
1
+ #
2
+ # The high-throughput toolkit (httk)
3
+ # Copyright (C) 2012-2024 the httk AUTHORS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ from .register import loaders
22
+
23
+
24
+ def load(filename: str, **kwargs: Any) -> Any:
25
+ ext = Path(filename).suffix.lower()
26
+ if ext:
27
+ return loaders.dispatch(ext, filename, **kwargs)
28
+ else:
29
+ raise Exception("Could not determine file type.")
httk/core/py.typed ADDED
File without changes
httk/core/register.py ADDED
@@ -0,0 +1,30 @@
1
+ #
2
+ # The high-throughput toolkit (httk)
3
+ # Copyright (C) 2012-2024 the httk AUTHORS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+
19
+ from ._plugins import PluginRegistry
20
+
21
+ loaders = PluginRegistry()
22
+
23
+
24
+ def register_loader(*, name: str, loader: str, extensions: tuple[str, ...]) -> None:
25
+ for ext in extensions:
26
+ loaders.register(key=ext.lower(), handler=loader, name=name)
27
+
28
+
29
+ def known_extensions() -> list[str]:
30
+ return loaders.keys()
@@ -0,0 +1,5 @@
1
+ from .backend import Backend
2
+ from .unwrapping import unwrap
3
+ from .view import View
4
+
5
+ __all__ = ["Backend", "View", "unwrap"]
@@ -0,0 +1,49 @@
1
+ from abc import ABC
2
+ from typing import Any, ClassVar, Self, cast
3
+
4
+
5
+ class Backend[BackendT: Backend](ABC):
6
+ """
7
+ Abstract base class to be subclassed into classes that keep track of alternative
8
+ representations of certain types of data, all adhering to a common API interface.
9
+
10
+ The class variable backend_classes is a list of all classes that can carry the kind of data the subclass represents.
11
+
12
+ A system of "hints" are used primarily to disambiguate between multiple valid interpretations of the same input object.
13
+ Unless otherwise documented for a specific backend, extra hints that do not affect this interpretation are ignored.
14
+
15
+ A set of backends are meant to be combined with a set of Views.
16
+ """
17
+
18
+ # Subclasses must implement this class variable, which only appear as a type annotation here
19
+ backend_classes: ClassVar[list[type["Backend[Any]"]]]
20
+
21
+ @classmethod
22
+ def create(cls: type[Self], obj: Any, **hints: Any) -> Self:
23
+ """
24
+ Given a source data (obj) and a set of hints, create a backend from one of the alternatives in the class variable `backend_classes`.
25
+
26
+ By design this creation depends heavily on order of the classes in `backend_classes`.
27
+ Each class is tried in the order they appear until one of them is successful, in the sense that their `__new__` does not return None.
28
+ Sometimes multiple backend classes can handle the same input type. In that case, dispatch is guided by keyword arguments `**hints`,
29
+ with the convention that:
30
+
31
+ * if a hint named `kind` is given, a backend class should accept the object only if it matches that `kind`;
32
+ * when `kind` matches, additional unrecognized hints may be ignored.
33
+ """
34
+ for t in cls.backend_classes:
35
+ instance = t(obj, **hints)
36
+ if instance is not None:
37
+ return cast(Self, instance)
38
+ raise TypeError(f"Cannot represent {type(obj)} as {cls.__name__}")
39
+
40
+ def __init__(self, backend, **hints) -> None:
41
+ pass
42
+
43
+ def unwrap(self) -> Any:
44
+ """
45
+ Return the most raw representation possible of this backend, i.e., if it uses a backend with an internal representaion -
46
+ or if it can (possibly lossly) convert itself into a more raw representation that still would be recognized as a <Something>Like type,
47
+ that representation will be returned. If this is not possible, the instance itself is returned.
48
+ """
49
+ return self
@@ -0,0 +1,12 @@
1
+ from typing import Any
2
+
3
+
4
+ def unwrap(obj: Any) -> Any:
5
+ """
6
+ Given a Backend or a View, return the most raw representation possible, i.e., if the backend has an internal representaion -
7
+ or if it can (possibly lossly) convert itself into a more raw representation that still would be recognized as a <Something>Like type,
8
+ that representation will be returned. If this is not possible, the instance itself is returned.
9
+ """
10
+ if hasattr(obj, "unwrap"):
11
+ return obj.unwrap()
12
+ return obj
@@ -0,0 +1,56 @@
1
+ from typing import Any, ClassVar
2
+
3
+ from .backend import Backend
4
+ from .unwrapping import unwrap
5
+
6
+
7
+ class View[BackendT: Backend]:
8
+ """
9
+ A set of views allow manipulating data and state of a backend through different interfaces.
10
+ Hence, creating a View from a Backend, or from another View, allows to read and operate on the data through the interface of that view,
11
+ even if it is not the natural representation of the underlying data.
12
+
13
+ Important: views are always meant to reference the data and state of *the same* underlying object, hence, e.g.:
14
+
15
+ * If a function is given an X object, and the function applies an Xvariant1View and then calls, e.g., close() via that view,
16
+ the excpectation should be that the original X object is now also closed.
17
+ * When, e.g., a TextstreamStringView is created on an already partially read stream, only the unread data will appear through that string interface.
18
+
19
+ All backends and views of the same kind of data (X) should be combined into a type union XLike that functions use to declare they support this kind of data.
20
+ Such functions should start with creating a View on the passed data, giving them access to the data in a single desired format.
21
+ """
22
+
23
+ # Python typing, and mypy in particular, have trouble with variables being assigned abstract base classes
24
+ _backend_base_cls: ClassVar[Any] # Subclass of Backend that defines a set of backends for similar data
25
+ _view_base_cls: ClassVar[Any] # Subclass of view that defines a set of views of similar data
26
+ _backend: BackendT
27
+
28
+ @classmethod
29
+ def _prepare_backend(
30
+ cls,
31
+ obj: Any,
32
+ hints: dict[str, Any],
33
+ ) -> BackendT:
34
+ """
35
+ Normalize an arbitrary backend/view/raw object into a backend suitable for constructing a view.
36
+
37
+ Behavior:
38
+ - if `obj` is already one of the accepted views, unwrap its underlying backend to adopt it;
39
+ - if `obj` is a backend inheriting from the right superclass, return it unchanged.
40
+ - if `obj` otherwise try to create a backend from that superclass via `backend_cls.create(obj, **hints)`;
41
+
42
+ `hints` are forwarded for backend selection/disambiguation.
43
+ """
44
+ if isinstance(obj, cls._view_base_cls):
45
+ return obj._backend
46
+ if not isinstance(obj, cls._backend_base_cls):
47
+ return cls._backend_base_cls.create(obj, **hints)
48
+ return obj
49
+
50
+ def unwrap(self) -> Any:
51
+ """
52
+ Return the most raw representation possible of this view, i.e., if it uses a backend with an internal representaion -
53
+ or if it can (possibly lossly) convert itself into a more raw representation that still would be recognized as a <Something>Like type,
54
+ that representation will be returned. If this is not possible, the instance itself is returned.
55
+ """
56
+ return unwrap(self._backend)
File without changes
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.4
2
+ Name: httk-core
3
+ Version: 2.0.0
4
+ Summary: httk₂ core functionality
5
+ Author-email: Rickard Armiento <rickard-gpg@armiento.net>
6
+ License-Expression: AGPL-3.0-or-later
7
+ Project-URL: Homepage, https://httk.org
8
+ Project-URL: Documentation, https://docs.httk.org/httk-core/
9
+ Project-URL: Repository, https://github.com/httk/httk-core
10
+ Project-URL: Issues, https://github.com/httk/httk-core/issues
11
+ Keywords: Materials Discovery,Materials Informatics,Materials Science
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
19
+ Classifier: Topic :: Scientific/Engineering :: Physics
20
+ Requires-Python: >=3.12
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.0; extra == "dev"
25
+ Requires-Dist: black; extra == "dev"
26
+ Requires-Dist: ruff; extra == "dev"
27
+ Requires-Dist: isort; extra == "dev"
28
+ Requires-Dist: pyright; extra == "dev"
29
+ Requires-Dist: mypy; extra == "dev"
30
+ Provides-Extra: docs
31
+ Requires-Dist: sphinx; extra == "docs"
32
+ Requires-Dist: furo; extra == "docs"
33
+ Requires-Dist: myst-parser; extra == "docs"
34
+ Requires-Dist: myst-nb; extra == "docs"
35
+ Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
36
+ Requires-Dist: sphinx-copybutton; extra == "docs"
37
+ Requires-Dist: sphinx-autoapi; extra == "docs"
38
+ Provides-Extra: release
39
+ Requires-Dist: build>=1.2; extra == "release"
40
+ Requires-Dist: twine>=6.0; extra == "release"
41
+ Dynamic: license-file
42
+
43
+ # httk-core
44
+
45
+ ![Status: Pre-release](https://img.shields.io/badge/status-pre--release-red)
46
+
47
+ > **⚠️ PRE-RELEASE**
48
+ >
49
+ > The version assigned to *httk-core* designates the overall version of *httk₂*,
50
+ > and thus starts at v2.0.0. However, versions v2.0.* are to be considered
51
+ > prereleases, and semantic versioning will not be used until v2.1.0.
52
+
53
+ `httk-core` is the central lightweight dependency shared by httk₂ modules.
54
+
55
+ It provides:
56
+
57
+ - the `httk.core` package within the PEP 420 native `httk` namespace;
58
+ - shared view/backend and data-stream primitives; and
59
+ - plugin discovery and aggregate operations such as `httk.core.load`, to which
60
+ other httk modules can register handlers.
61
+
62
+ Most users should install the [`httk2`](https://github.com/httk/httk2)
63
+ metapackage, which selects a useful set of httk modules:
64
+
65
+ ```console
66
+ pip install httk2
67
+ ```
68
+
69
+ Install only the core package with:
70
+
71
+ ```console
72
+ pip install httk-core
73
+ ```
74
+
75
+ Other distributions, such as `httk-io`, install their own packages under the
76
+ same `httk` namespace and can then be imported as `httk.io`.
@@ -0,0 +1,49 @@
1
+ httk/core/__init__.py,sha256=YSARcnjJER8mFkrsQRiCHSOHDE_KATkuvn2E2mvt18I,2895
2
+ httk/core/_discover.py,sha256=pEWZs-Kn2nQe3p5Edhek676iVZ10rGePR80fEc4D_NM,1215
3
+ httk/core/_plugins.py,sha256=9CwgeF6G97tHyMOZkeJQZE_ZCQNZ5AIKUfkDYD9QV_M,3092
4
+ httk/core/dataloader.py,sha256=2lFzg8i5j3ovNg-O-pMH9a41LVynqNoEDIAGYmFfF_Q,10081
5
+ httk/core/loading.py,sha256=VUXGwSWLhtYEyF1b5XX9qgNfutDGkBPueUgm71pkX68,1081
6
+ httk/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ httk/core/register.py,sha256=c7ZHZ2guB9TmaYPRLr5rRZ3kNCZvbG0RSbTpI85vRak,1090
8
+ httk/core/datastream/__init__.py,sha256=PpDxhJrfeTW6UliPcMTcKutmZKO_3UsWANP5yYjjX84,2738
9
+ httk/core/datastream/bytestream_api.py,sha256=ad53pPAwZC17dfW0ldv9R6ZVl5QdEA_vqckgtr1utyI,871
10
+ httk/core/datastream/bytestream_backend.py,sha256=etGEfI1rMGLcxkgFM92yO9p3gnXd3ZK3L3SBZEMSLKQ,314
11
+ httk/core/datastream/bytestream_bytes.py,sha256=DiKzl0ivt5OOrtpM4q02krVqYgVmbU-74qmrUMgeVok,1646
12
+ httk/core/datastream/bytestream_bytes_view.py,sha256=tWZuThktCHFjq_X5ZDiXUCjEjgthJ0KC9QrHHYbUNVo,1107
13
+ httk/core/datastream/bytestream_common.py,sha256=LDIXkViHlrgzn2xQT2EeRVMhtzG9GCADv9rI7vr8Lb8,1134
14
+ httk/core/datastream/bytestream_file.py,sha256=DKwCs0EjEGP78UVMiTH8w5hL2c0GbUxtonQI4POxWvs,1834
15
+ httk/core/datastream/bytestream_file_view.py,sha256=bLpU_CH8fqlr2SBf-H18q09NXOMb7uIWoyvB7yFPQWA,5604
16
+ httk/core/datastream/bytestream_filename.py,sha256=6S_6fIHWiFiW3y7FERR7qzaXEZWkU7X2iWSokYI052M,1725
17
+ httk/core/datastream/bytestream_filename_view.py,sha256=w3OULUJvLJaHGvOP9l9v3Yen118J0IPNFaKnsYMnlrg,1245
18
+ httk/core/datastream/bytestream_like.py,sha256=U2RFkuVF9IQx7Ax3-bypx7sEhIeqMt5rFa-uUy3gF38,322
19
+ httk/core/datastream/bytestream_request.py,sha256=bW6BBIa3mgd7T8dYTymJ--x0rxvIIzArgrk_ZNTi4yA,2262
20
+ httk/core/datastream/bytestream_request_view.py,sha256=69fPVjRZY1qBqf0aeoCq93OL-obzyeHr0Ll-sPLhhkc,1816
21
+ httk/core/datastream/bytestream_url.py,sha256=ThzfvtvHz_YfL20lo6jlOB2rUGDQY0F-4y9q-CXWFZM,2410
22
+ httk/core/datastream/bytestream_url_view.py,sha256=ZqSVX4vIYLpIjqPdMDfXySqcSGM_kj7MelcU3iMBYwY,1251
23
+ httk/core/datastream/bytestream_view.py,sha256=g87V1HQXFRUZGUBgyk3mWCjim7jVqsfPFcRqq3AgC5k,436
24
+ httk/core/datastream/compression.py,sha256=P6d0lyXjbWn7wYYyxhKs944dTakWzBV6ieVJgUvQeMk,6966
25
+ httk/core/datastream/textstream_api.py,sha256=7ltSE1eiCufUea3dfPlr1FWPyczzbLB2-gWV1Aezboo,868
26
+ httk/core/datastream/textstream_backend.py,sha256=_tfj1KfgXOUhByT4ueW9wAfuqFV4tImDPqAkzp19atY,314
27
+ httk/core/datastream/textstream_common.py,sha256=nYupIVL_g3CdCx_Dw3W2K8NdGLlJb2UcXeaJQtqHBWU,1079
28
+ httk/core/datastream/textstream_file.py,sha256=1ukkIWQF2YIEcgHaR2AFrL6vojQ_Mxqy3_dF5k5JsKM,1462
29
+ httk/core/datastream/textstream_file_view.py,sha256=0qvTXYiXEwJRKIwExK4lY3FPz5AKE-3_31-mX5W5gS8,6420
30
+ httk/core/datastream/textstream_filename.py,sha256=AVuUlmQ8ItsJ78F3msV7uqGmxigerZTx5tq0dHCXns0,1902
31
+ httk/core/datastream/textstream_filename_view.py,sha256=2E_e0DKE_XeitInKOKIJHxLo0qlSm-Y8cvRsDSHeMvs,1332
32
+ httk/core/datastream/textstream_like.py,sha256=R2eGKVWGS12i5thtjA8dqpuvkBlVE0rFqqtPwyLvMDY,299
33
+ httk/core/datastream/textstream_request.py,sha256=DPFDBHUjttFBd3YZYox6KOC74udajOglnIEFVCBP6Pc,2505
34
+ httk/core/datastream/textstream_request_view.py,sha256=EflFJJ3dgUhUmuFblIauwoUY5Rsv088hwIaDf6RxjJk,1816
35
+ httk/core/datastream/textstream_string.py,sha256=VipgoYgLiSEsePzhgs0xbbxwFwIWgQqI6lEwkj2HlzI,1334
36
+ httk/core/datastream/textstream_string_view.py,sha256=z9NxkXjRD62QqJW3-inMPRi7lIBNfr7tjuAugD9nmTE,1111
37
+ httk/core/datastream/textstream_url.py,sha256=j1gjNQKlIUiGDyxVMLRC1mLKZIeTAOWmGZkUWB6R8oc,2653
38
+ httk/core/datastream/textstream_url_view.py,sha256=DOUovDax_JevCB8gkLAYAoLRGpblS1ddO-vLZKGEub8,1251
39
+ httk/core/datastream/textstream_view.py,sha256=S4lgenptFquuO5gqAc7R2GBQkjQgXhCR9IJzHmdhg2E,436
40
+ httk/core/views/__init__.py,sha256=o2794D3HSQPwIqP31ujvgceR_iHqS1-xAMoQTvl0mYs,124
41
+ httk/core/views/backend.py,sha256=m2EMCK9A8a_j216lIN024wv8IIjAkFf-IKLI2Lu6wEg,2485
42
+ httk/core/views/unwrapping.py,sha256=VyFC-QWokBuDHpTCmSBRVjQgQIe9nF-TWm7joo7uZTc,512
43
+ httk/core/views/view.py,sha256=yL3ML_OX26vFodTSDCtqkU5uHZ24c08HwOpxjDNOWs8,2892
44
+ httk/handlers/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ httk_core-2.0.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
46
+ httk_core-2.0.0.dist-info/METADATA,sha256=_6od5xP_hrLJln7gLUYUVRsiA4yguBnDq7radPTr2VI,2756
47
+ httk_core-2.0.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
48
+ httk_core-2.0.0.dist-info/top_level.txt,sha256=UBbnLJzPWLPWOl6JzJzeLuWa3uq7iStjxrP4741hFRg,5
49
+ httk_core-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+