submine 0.1.2__cp310-cp310-macosx_11_0_arm64.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 (47) hide show
  1. submine/__init__.py +37 -0
  2. submine/algorithms/__init__.py +23 -0
  3. submine/algorithms/base.py +143 -0
  4. submine/algorithms/gspan.py +156 -0
  5. submine/algorithms/gspan_cpp.cpython-310-darwin.so +0 -0
  6. submine/algorithms/sopagrami.py +251 -0
  7. submine/algorithms/sopagrami_cpp.cpython-310-darwin.so +0 -0
  8. submine/api.py +134 -0
  9. submine/backends/__init__.py +0 -0
  10. submine/backends/gspan/CMakeLists.txt +65 -0
  11. submine/backends/gspan/dfs.cpp +98 -0
  12. submine/backends/gspan/graph.cpp +165 -0
  13. submine/backends/gspan/gspan.cpp +776 -0
  14. submine/backends/gspan/gspan.h +296 -0
  15. submine/backends/gspan/ismin.cpp +124 -0
  16. submine/backends/gspan/main.cpp +106 -0
  17. submine/backends/gspan/misc.cpp +177 -0
  18. submine/backends/gspan/python_bindings.cpp +133 -0
  19. submine/backends/sopagrami/cpp/CMakeLists.txt +44 -0
  20. submine/backends/sopagrami/cpp/include/alg.hpp +150 -0
  21. submine/backends/sopagrami/cpp/include/common/timer.hpp +18 -0
  22. submine/backends/sopagrami/cpp/src/alg.cpp +805 -0
  23. submine/backends/sopagrami/cpp/src/dump.cpp +262 -0
  24. submine/backends/sopagrami/cpp/src/main.cpp +94 -0
  25. submine/backends/sopagrami/cpp/src/python_bindings.cpp +123 -0
  26. submine/cli/__init__.py +6 -0
  27. submine/cli/main.py +87 -0
  28. submine/core/__init__.py +12 -0
  29. submine/core/graph.py +180 -0
  30. submine/core/result.py +121 -0
  31. submine/datasets/__init__.py +11 -0
  32. submine/datasets/loaders.py +145 -0
  33. submine/errors.py +41 -0
  34. submine/io/__init__.py +30 -0
  35. submine/io/common.py +173 -0
  36. submine/io/gexf.py +88 -0
  37. submine/io/gspan.py +268 -0
  38. submine/io/sopagrami.py +143 -0
  39. submine/io/transcode.py +147 -0
  40. submine/registry.py +8 -0
  41. submine/utils/__init__.py +6 -0
  42. submine/utils/checks.py +115 -0
  43. submine/utils/logging.py +41 -0
  44. submine-0.1.2.dist-info/METADATA +196 -0
  45. submine-0.1.2.dist-info/RECORD +47 -0
  46. submine-0.1.2.dist-info/WHEEL +6 -0
  47. submine-0.1.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,115 @@
1
+ """Environment and safety checks for submine.
2
+
3
+ This module centralizes lightweight validation utilities that protect the
4
+ library from common failure modes (corrupt inputs, missing binaries) and
5
+ from avoidable abuse (e.g., attempting to load arbitrarily large files into
6
+ memory, unsafe subprocess execution defaults).
7
+
8
+ These are not meant to be a sandbox. They are meant to provide sensible,
9
+ defensive defaults for a publishable OSS library.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import shutil
16
+ from pathlib import Path
17
+ from typing import Optional
18
+
19
+
20
+ DEFAULT_MAX_INPUT_MB = int(os.getenv("SUBMINE_MAX_INPUT_MB", "128"))
21
+ DEFAULT_MAX_INPUT_BYTES = DEFAULT_MAX_INPUT_MB * 1024 * 1024
22
+
23
+
24
+ DEFAULT_MAX_LINES = int(os.getenv("SUBMINE_MAX_LINES", "5000000")) # 5M lines
25
+ DEFAULT_MAX_LINE_BYTES = int(os.getenv("SUBMINE_MAX_LINE_BYTES", "1048576")) # 1 MiB per line
26
+
27
+
28
+ def iter_text_lines(
29
+ path: str | Path,
30
+ *,
31
+ encoding: str = "utf-8",
32
+ max_lines: int | None = None,
33
+ max_line_bytes: int | None = None,
34
+ ):
35
+ """Yield decoded lines from *path* with hard limits.
36
+
37
+ This is intended for parsers that stream large graph files. It protects
38
+ against:
39
+ - extremely long lines (often accidental corruption or malicious inputs)
40
+ - unbounded files that could consume excessive CPU time
41
+
42
+ Notes:
43
+ - Lines are decoded with ``errors='replace'`` to avoid UnicodeDecodeError.
44
+ - The returned lines are stripped of trailing ``\n``.
45
+ """
46
+ # Resolve limits at call time so test suites (and embedding apps) can
47
+ # override them via environment variables without requiring a reload.
48
+ if max_lines is None:
49
+ max_lines = int(os.getenv("SUBMINE_MAX_LINES", str(DEFAULT_MAX_LINES)))
50
+ if max_line_bytes is None:
51
+ max_line_bytes = int(os.getenv("SUBMINE_MAX_LINE_BYTES", str(DEFAULT_MAX_LINE_BYTES)))
52
+
53
+ p = assert_regular_file(path)
54
+ count = 0
55
+ with p.open("rb") as f:
56
+ for raw in f:
57
+ count += 1
58
+ if count > max_lines:
59
+ from ..errors import ResourceLimitError
60
+ raise ResourceLimitError(
61
+ f"Refusing to parse {p}: exceeds max line count limit ({max_lines}). "
62
+ "Set SUBMINE_MAX_LINES to increase the limit if you trust this input."
63
+ )
64
+ if len(raw) > max_line_bytes:
65
+ from ..errors import ResourceLimitError
66
+ raise ResourceLimitError(
67
+ f"Refusing to parse {p}: line {count} exceeds max line length ({max_line_bytes} bytes). "
68
+ "Set SUBMINE_MAX_LINE_BYTES to increase the limit if you trust this input."
69
+ )
70
+ yield raw.decode(encoding, errors="replace").rstrip("\n")
71
+
72
+ def is_tool_available(name: str) -> bool:
73
+ """Return True if a given executable exists on the system PATH."""
74
+ return shutil.which(name) is not None
75
+
76
+
77
+ def assert_regular_file(path: str | Path, *, must_exist: bool = True) -> Path:
78
+ """Validate that *path* points to a regular file.
79
+
80
+ We reject directories and (by default) require existence. We also resolve
81
+ symlinks to avoid surprises.
82
+ """
83
+ p = Path(path).expanduser()
84
+ if must_exist and not p.exists():
85
+ raise FileNotFoundError(f"File does not exist: {p}")
86
+ if must_exist and not p.is_file():
87
+ raise ValueError(f"Expected a regular file path, got: {p}")
88
+ # Resolve to eliminate '..' segments and follow symlinks.
89
+ return p.resolve()
90
+
91
+
92
+ def assert_file_size_under(path: str | Path, *, max_bytes: int = DEFAULT_MAX_INPUT_BYTES) -> None:
93
+ """Raise if the file exceeds *max_bytes*.
94
+
95
+ This is primarily used to protect code paths that necessarily read the full
96
+ file into memory (e.g., certain bindings).
97
+ """
98
+ p = Path(path)
99
+ try:
100
+ size = p.stat().st_size
101
+ except OSError as e:
102
+ raise OSError(f"Unable to stat file: {p}") from e
103
+ if size > max_bytes:
104
+ from ..errors import ResourceLimitError
105
+ raise ResourceLimitError(
106
+ f"Refusing to load {p} ({size} bytes): exceeds configured limit of {max_bytes} bytes. "
107
+ "Set SUBMINE_MAX_INPUT_MB to increase the limit if you trust this input."
108
+ )
109
+
110
+
111
+ def safe_read_text(path: str | Path, *, encoding: str = "utf-8", max_bytes: int = DEFAULT_MAX_INPUT_BYTES) -> str:
112
+ """Read a text file with a hard cap on bytes."""
113
+ p = assert_regular_file(path)
114
+ assert_file_size_under(p, max_bytes=max_bytes)
115
+ return p.read_text(encoding=encoding, errors="replace")
@@ -0,0 +1,41 @@
1
+ """Logging utilities for submine.
2
+
3
+ This module configures a simple hierarchical logging setup that
4
+ suppresses log output by default. Users can enable verbose logging
5
+ per algorithm by passing ``verbose=True`` to the constructor. All
6
+ modules within submine should obtain their logger via
7
+ :func:`get_logger` rather than calling :func:`logging.getLogger`
8
+ directly.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ from typing import Optional
15
+
16
+
17
+ def get_logger(name: Optional[str] = None) -> logging.Logger:
18
+ """Return a logger with a default configuration.
19
+
20
+ If called for the first time this function sets up a root logger
21
+ with a basic configuration that logs messages with level WARNING and
22
+ above to stderr. Subsequent calls return child loggers that
23
+ propagate messages to the root.
24
+
25
+ Parameters
26
+ ----------
27
+ name: str, optional
28
+ Name of the logger. If omitted, a root logger is returned.
29
+
30
+ Returns
31
+ -------
32
+ logging.Logger
33
+ Configured logger instance.
34
+ """
35
+ if not logging.getLogger().handlers:
36
+ # Configure root logger once
37
+ logging.basicConfig(
38
+ level=logging.WARNING,
39
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
40
+ )
41
+ return logging.getLogger(name)
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.2
2
+ Name: submine
3
+ Version: 0.1.2
4
+ Summary: Modular subgraph mining library with unified API
5
+ Keywords: graph-mining,subgraph-mining,gspan,frequent-subgraph-mining
6
+ Author: Ridwan Amure
7
+ License: MIT
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Project-URL: Homepage, https://github.com/instabaines/submine
13
+ Project-URL: Repository, https://github.com/instabaines/submine
14
+ Project-URL: Issues, https://github.com/instabaines/submine/issues
15
+ Requires-Python: >=3.9
16
+ Requires-Dist: networkx>=2.8
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=7; extra == "dev"
19
+ Requires-Dist: pytest-cov>=4; extra == "dev"
20
+ Requires-Dist: build>=1; extra == "dev"
21
+ Requires-Dist: twine>=5; extra == "dev"
22
+ Requires-Dist: ruff>=0.5; extra == "dev"
23
+ Description-Content-Type: text/markdown
24
+
25
+ # submine
26
+
27
+ **submine** is a research‑grade Python library for frequent subgraph mining that provides a unified, safe, and extensible interface over heterogeneous mining algorithms implemented in Python, C++, and Java.
28
+
29
+ The goal of *submine* is to let users focus on **what** to mine rather than **how** each algorithm expects its input. Users select an algorithm and parameters; *submine* automatically validates inputs, converts graph formats, and executes the backend in a controlled and reproducible manner.
30
+
31
+ ---
32
+
33
+ ## Key Features
34
+
35
+ * **Algorithm‑centric API**
36
+ You specify the mining algorithm and parameters; *submine* handles format adaptation and execution.
37
+
38
+ * **Direct format transcoding (no redundant rewrites)**
39
+ Input graphs are converted directly into the native format required by the selected algorithm.
40
+
41
+ * **Multi‑format graph support**
42
+ Edge lists, gSpan datasets, single‑graph `.lg` files, and GEXF are supported out of the box.
43
+
44
+ * **Safe and reproducible execution**
45
+ Parameter validation, deterministic format detection, and hardened subprocess execution are enforced by default.
46
+
47
+ * **Extensible design**
48
+ New algorithms can be added via a clean backend interface without modifying core logic.
49
+
50
+ ---
51
+
52
+ ## Supported Algorithms
53
+
54
+ ### gSpan (Frequent Subgraph Mining)
55
+
56
+ * **Graph type:** Multiple graphs (transactional dataset)
57
+ * **Typical use case:** Discovering frequent substructures across many graphs
58
+ * **Backend:** C++
59
+
60
+ The gSpan backend in *submine* is a C++ implementation adapted and extended from the widely used **gBoost / gSpan reference implementations**, with additional input validation, format handling, and Python bindings for safe integration.
61
+
62
+ ### SoPaGraMi (Single‑Graph Pattern Mining)
63
+
64
+ * **Graph type:** Single large graph
65
+ * **Typical use case:** Social, biological, or information networks
66
+ * **Backend:** C++
67
+
68
+ SoPaGraMi is used for scalable subgraph mining on a single graph, where frequency is defined structurally rather than transactionally.
69
+
70
+ ---
71
+
72
+ ## Supported Input Formats
73
+
74
+ *submine* automatically detects the input format and converts it to the format required by the chosen algorithm:
75
+
76
+ * **Edge lists**: `.txt`, `.edgelist`
77
+ * **gSpan datasets**: `.data`, `.data.x`, `.data.N`
78
+ * **SoPaGraMi graphs**: `.lg`
79
+ * **GEXF**: `.gexf`
80
+
81
+ Format detection is deterministic and does not rely on user‑supplied flags.
82
+
83
+ ---
84
+
85
+ ## Installation
86
+
87
+ ### Standard installation
88
+
89
+ ```bash
90
+ pip install submine
91
+ ```
92
+
93
+ ### Development installation
94
+
95
+ ```bash
96
+ pip install -e ".[dev]"
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Basic Usage
102
+
103
+ ### gSpan example
104
+
105
+ ```python
106
+ from submine.api import mine_subgraphs
107
+
108
+ results = mine_subgraphs(
109
+ data="graphs.data",
110
+ algorithm="gspan",
111
+ min_support=5
112
+ )
113
+ ```
114
+
115
+ **Parameters**
116
+
117
+ * `data` (str or path): Path to the input graph dataset
118
+ * `algorithm` (str): Mining algorithm (`"gspan"`, `"sopagrami"`, …)
119
+ * `min_support` (int): Minimum support threshold (algorithm‑specific semantics)
120
+
121
+ ---
122
+
123
+ ### SoPaGraMi example
124
+
125
+ ```python
126
+ results = mine_subgraphs(
127
+ data="citeseer.lg",
128
+ algorithm="sopagrami",
129
+ min_support=100,
130
+ sorted_seeds=4,
131
+ dump_images_csv=True,
132
+ dump_sample_embeddings=False,
133
+ out_dir="."
134
+ )
135
+ ```
136
+
137
+ **SoPaGraMi‑specific parameters**
138
+
139
+ * `min_support` (int): Minimum frequency threshold
140
+ * `sorted_seeds` (int): Seed sorting strategy (implementation‑specific)
141
+ * `dump_images_csv` (bool): Whether to dump pattern images as CSV metadata
142
+ * `dump_sample_embeddings` (bool): Whether to dump sample embeddings (experimental)
143
+ * `out_dir` (str or path): Output directory for results (default: `./sopagrami_result`)
144
+
145
+ ---
146
+
147
+ ## Design Philosophy
148
+
149
+ * **No algorithm‑specific I/O burden on the user**
150
+ Users never manually convert graph formats.
151
+
152
+ * **Minimal assumptions about graph structure**
153
+ Directed/undirected and labeled/unlabeled graphs are handled at the backend level.
154
+
155
+ * **Research‑grade transparency**
156
+ Backends are explicitly documented and citable.
157
+
158
+ ---
159
+
160
+ ## Citation
161
+
162
+ If you use **gSpan**, please cite:
163
+
164
+ ```bibtex
165
+ @inproceedings{yan2002gspan,
166
+ title={gspan: Graph-based substructure pattern mining},
167
+ author={Yan, Xifeng and Han, Jiawei},
168
+ booktitle={Proceedings of the IEEE International Conference on Data Mining},
169
+ pages={721--724},
170
+ year={2002}
171
+ }
172
+ ```
173
+
174
+ If you use **SoPaGraMi**, please cite:
175
+
176
+ ```bibtex
177
+ @article{nguyen2020fast,
178
+ title={Fast and scalable algorithms for mining subgraphs in a single large graph},
179
+ author={Nguyen, Lam BQ and Vo, Bay and Le, Ngoc-Thao and Snasel, Vaclav and Zelinka, Ivan},
180
+ journal={Engineering Applications of Artificial Intelligence},
181
+ volume={90},
182
+ pages={103539},
183
+ year={2020}
184
+ }
185
+ ```
186
+
187
+ To cite this library:
188
+
189
+ ```bibtex
190
+ @misc{amure_submine,
191
+ title = {submine: A Unified Subgraph Mining Library},
192
+ author = {Amure, Ridwan},
193
+ year = {2025},
194
+ url = {https://github.com/instabaines/submine}
195
+ }
196
+ ```
@@ -0,0 +1,47 @@
1
+ submine/registry.py,sha256=Jr8tbfbU_j5L5waH7gl4dqtrNB0X_boHnu3A0bVMmuo,254
2
+ submine/__init__.py,sha256=tnuVK76tHfOrZPntQLD-iUugR5PyyGGaE0kyrY2VRjI,899
3
+ submine/api.py,sha256=mGy1ynLFyWnj4yv1piDbBkebNE6GEw4FztwhrKsp4Ao,4369
4
+ submine/errors.py,sha256=YaEtNKkN4qpyrbhSLaN3Vkcot6sfG-9atiYPo8HWds8,1112
5
+ submine/core/graph.py,sha256=XZgLuXh4NOKaSGcBPpFoA8ZRvMkNjmWozVRL7xzowUs,6809
6
+ submine/core/__init__.py,sha256=prISqLxgIC4zUO39LDC69AhSrK4tGl1iC0BmKzuxL_A,443
7
+ submine/core/result.py,sha256=t9D1sYYWr-vdVJGw5AX-CSbiuSioVrrmbY3EeT54mX8,3653
8
+ submine/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ submine/backends/gspan/CMakeLists.txt,sha256=etRcErf0JK9w7xj_OghoFGszpiQooETHPZE4UIu3RvA,1586
10
+ submine/backends/gspan/misc.cpp,sha256=w5Z2XvogkSNqskZezxauvii-1H0lqZ_RWIpEWfQK5LI,4961
11
+ submine/backends/gspan/graph.cpp,sha256=zhtw3vXcOIFk1sZpoIOQUFIey6teLmPvCeQ_2LiD98w,4265
12
+ submine/backends/gspan/gspan.h,sha256=w9rPfrOB1JIi1M1AlD02PKsrELkRwC3uvcFc0jrLIz4,8247
13
+ submine/backends/gspan/dfs.cpp,sha256=sBsixi3CzhmnClbzmFDj9DOYQ-eaeplL3zVIO7q1lhI,2593
14
+ submine/backends/gspan/ismin.cpp,sha256=MKYMYgnMIMaiAKZLIdNjEe7eUQ2E_mGzo6phOcDmiuM,4083
15
+ submine/backends/gspan/gspan.cpp,sha256=9iud5jf23oyUyjNWe_z_r94f48tnqK5EWQceWUfGIgk,21101
16
+ submine/backends/gspan/python_bindings.cpp,sha256=IrOhBJiYjMOa90GEzncxeBZ1VkZ5-wkJHZB-cqNHZsI,3912
17
+ submine/backends/gspan/main.cpp,sha256=ipp3kPAssz-BBu_7rDi6GfPWwKjJfkJYIv74Rl5DH_A,3414
18
+ submine/backends/sopagrami/cpp/CMakeLists.txt,sha256=WXOoHkclzxmcBCyRBe_D-wV1bXM48CQeKioE-ZvR0Rk,1263
19
+ submine/backends/sopagrami/cpp/include/alg.hpp,sha256=D8krOTYxX47DS3ur_2fwyYYROmv1nXuM6rDiMx5spw8,5284
20
+ submine/backends/sopagrami/cpp/include/common/timer.hpp,sha256=h3wUdSJU_tpoXf5NVtmcUj92wBcNgUPUKEuLcCGiz3Q,360
21
+ submine/backends/sopagrami/cpp/src/alg.cpp,sha256=wd8CTdfUS7iemG6OCWn3mNtujJNplaf561Q0SCzynGo,28710
22
+ submine/backends/sopagrami/cpp/src/python_bindings.cpp,sha256=e9OsCPhaFdkBmqTYSVMXLwFTrg2ig0nebw5dKUxtBqM,3356
23
+ submine/backends/sopagrami/cpp/src/main.cpp,sha256=oiI4I73bKqj_IbuNjEI4WHZL8M9pvIVlJwhcGgSGbHA,3197
24
+ submine/backends/sopagrami/cpp/src/dump.cpp,sha256=0knEeEzPQXVOCJhvU3i4ELIfXdeJsTQUG1OTHxVXtN4,8942
25
+ submine/datasets/loaders.py,sha256=USiGS45-5DwshmJsx0f788Pb-471xcB7np31OUzkm5E,4952
26
+ submine/datasets/__init__.py,sha256=JggD0frQ9zZh9iXINpNMGvTkFac1DZa3U-Gf8umvnmE,384
27
+ submine/io/gexf.py,sha256=jdJYTgf3WyLIhIP-8m4b9JJbGGo2_PvAQ1ddwVGKBLU,2695
28
+ submine/io/transcode.py,sha256=9lj5QOZVz1Q6SQTCWGWlxEzjOfVTqFNzFQ026hVotG4,4428
29
+ submine/io/__init__.py,sha256=VAuJCGXE29KGGSq3jHGOV0mtYLS_yMcg_v-7ESHQze4,697
30
+ submine/io/gspan.py,sha256=B49RPwsSurN-rbPJUZppzJ_5hFzGgwBF9UYsTv3h_wI,8499
31
+ submine/io/common.py,sha256=afDqYSBco4DH8vuhRIZd0SKy_AqFPrrSOWNja5Of7lU,4905
32
+ submine/io/sopagrami.py,sha256=qYqZvKpUmSxj8HDzn7IxNnhu11LRFcMX4NCKro4NIpw,4950
33
+ submine/algorithms/gspan_cpp.cpython-310-darwin.so,sha256=Spzdgpwgb0yjWAgKBVVvtuqAAhQ7Fp399zdg6EBsGxc,188080
34
+ submine/algorithms/__init__.py,sha256=2OvRa_DdtsGbE-Yf_FF0Q6XadS9wTSh2mlYfrc6Yl9s,953
35
+ submine/algorithms/gspan.py,sha256=EjAGJCo7Y4XmX_ekJXhqPhAvxuCDJHTvBOviMoZxpds,5662
36
+ submine/algorithms/sopagrami_cpp.cpython-310-darwin.so,sha256=cjDt-KiR_c9E0WjwFY7nPu76_S3snm6NZOOW8xki8qc,274080
37
+ submine/algorithms/sopagrami.py,sha256=AAr7QWDelIGjvRYefLpiu3upsu1hU50XthJxFd7cV1c,9219
38
+ submine/algorithms/base.py,sha256=YW2X021ZfHIhnggwMkzklh5KMdnN1HoQ1XB7MTfwmRk,5381
39
+ submine/utils/logging.py,sha256=zcx-noJ20Cl2sbaH1HZkKSZZyOXMfHB9JRbbVACJpIM,1275
40
+ submine/utils/checks.py,sha256=fFlxFyPNRfhztgoYXzvfaoELzHN8qQOCmuHUaUbLX2E,4519
41
+ submine/utils/__init__.py,sha256=pF9-ISTFv1XH4g7RiVz2Dapt1EdJBUNleJ7mhpELlGA,196
42
+ submine/cli/__init__.py,sha256=k3MAv7_6UTA7O0Kp1FaMBd0veB0I3MNHdOsHql2oYG8,246
43
+ submine/cli/main.py,sha256=VkdlRALYUzSrgMOL3mBpNsrrfgluajvNKbPsScNL-lQ,2555
44
+ submine-0.1.2.dist-info/RECORD,,
45
+ submine-0.1.2.dist-info/WHEEL,sha256=ARvpZEbgFu-cAxZINNemqHWSYlsYgHkQqksAmhPJqFw,141
46
+ submine-0.1.2.dist-info/METADATA,sha256=di2defMFjShQOcIaOc1dAMIZftY52T-qJ978bZ0neZI,5749
47
+ submine-0.1.2.dist-info/licenses/LICENSE,sha256=KNAADYhXKAIGySYjfCVq6P4ZDhIUFfHxeZFYa3-32ec,1055
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: scikit-build-core 0.11.6
3
+ Root-Is-Purelib: false
4
+ Tag: cp310-cp310-macosx_11_0_arm64
5
+ Generator: delocate 0.13.0
6
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.