corio 2.2.3__tar.gz → 2.2.4__tar.gz
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.
- {corio-2.2.3 → corio-2.2.4}/PKG-INFO +6 -1
- {corio-2.2.3 → corio-2.2.4}/corio/entrypoint.py +13 -0
- {corio-2.2.3 → corio-2.2.4}/corio/infra/releaser.py +20 -11
- {corio-2.2.3 → corio-2.2.4}/corio/pyproject.package.toml +9 -5
- corio-2.2.4/corio/tests/test_dns.py +89 -0
- corio-2.2.4/corio/tests/test_dt.py +10 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tests/test_env.py +10 -0
- corio-2.2.4/corio/tests/test_hash.py +18 -0
- corio-2.2.4/corio/tests/test_hook.py +28 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tests/test_infra.py +86 -0
- corio-2.2.4/corio/tests/test_iterator.py +48 -0
- corio-2.2.4/corio/tests/test_jsn.py +24 -0
- corio-2.2.4/corio/tests/test_name.py +20 -0
- corio-2.2.4/corio/tests/test_path.py +158 -0
- corio-2.2.4/corio/tests/test_patterns.py +71 -0
- corio-2.2.4/corio/tests/test_rand.py +46 -0
- corio-2.2.4/corio/tests/test_strings.py +54 -0
- corio-2.2.4/corio/tests/test_toml.py +37 -0
- corio-2.2.4/corio/tests/test_tools.py +14 -0
- corio-2.2.4/corio/tests/test_yaml.py +26 -0
- {corio-2.2.3 → corio-2.2.4}/corio.egg-info/PKG-INFO +6 -1
- {corio-2.2.3 → corio-2.2.4}/corio.egg-info/SOURCES.txt +11 -0
- {corio-2.2.3 → corio-2.2.4}/corio.egg-info/requires.txt +7 -0
- {corio-2.2.3 → corio-2.2.4}/pyproject.toml +9 -5
- corio-2.2.3/corio/tests/test_jsn.py +0 -13
- corio-2.2.3/corio/tests/test_path.py +0 -99
- corio-2.2.3/corio/tests/test_yaml.py +0 -13
- {corio-2.2.3 → corio-2.2.4}/LICENSE +0 -0
- {corio-2.2.3 → corio-2.2.4}/README.md +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/ai/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/ai/agentic.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/ai/infer.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/aio.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/api.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/augmentation.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/av.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/caching.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/constants.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/context.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/dataclass.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/datatype.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/db/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/db/document.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/debug.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/dm.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/dns/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/dns/client.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/dns/dm.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/dns/proxy.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/dns/server.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/docker/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/dt.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/encrypt.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/env.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/function.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/google_api.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/ha/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/ha/constants.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/ha/core.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/ha/supervisor.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/ha/utils.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/hash.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/hfh.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/hook.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/https.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/infra/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/infra/api.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/infra/incrementor_pyproject.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/infra/project.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/infra/repository.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/infra/stack.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/inherit.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/inspection.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/interface/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/interface/context.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/interface/controls.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/interface/interface.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/iterator.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/jsn.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/json_fix.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/logs.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/markup.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/merging.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/metric.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/mqtt.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/name.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/net.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/netrc.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/openai.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/parallel.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/path/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/path/app.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/path/path.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/path/type.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/paths.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/patterns.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/pdf.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/plat.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/process.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/profiling.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/rand.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/sec.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/semantic.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/sets.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/setup/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/spaces.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/strings.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tabular.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tests/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tests/conftest.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tests/helpers.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tests/test_caching.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tests/test_datatype.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tests/test_encrypt.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tokenization.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/toml.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/tools.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/unicode.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/version/__init__.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/version/version.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/webhook.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/yml.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio/youtube.py +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio.egg-info/dependency_links.txt +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio.egg-info/entry_points.txt +0 -0
- {corio-2.2.3 → corio-2.2.4}/corio.egg-info/top_level.txt +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/add-service +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/add-user-path +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/add-ve-shell +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/apt-essentials +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/apt-headless +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/auth-token +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/compose-update +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/compress-dir +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/cru +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/docker-build-bases +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/docker-build-bases-dev +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/docker-install-deps +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/docker-install-prod +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/docker-prune +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/docker-sandbox +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/docker-sandbox-init +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/docs-deploy +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/download +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/encrypt-secrets +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/fmtr-test-script +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/git-clone +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/ha-addon-launch +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/infra +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/infra-sync +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/install-browser +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/install-docker +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/install-ts +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/install-ys +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/mirror-dir +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/opt-dev-init +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/parse-args +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/pypi-check +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/pypi-reserve +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/run-script +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/set-password +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/set-secure-path +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/set-user-sudo +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/snips-install +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/ssh-auth +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/ssh-serve +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/tasmota-config +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/tasmota-flash +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/tasmota-terminal +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/vlc-tn +0 -0
- {corio-2.2.3 → corio-2.2.4}/scripts/vm-launch +0 -0
- {corio-2.2.3 → corio-2.2.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: corio
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Summary: Collection of high-level tools to simplify everyday development tasks, with a focus on AI/ML
|
|
5
5
|
Author-email: Frontmatter AI <innovative.fowler@mask.pro.fmtr.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -188,6 +188,8 @@ Requires-Dist: httpx; extra == "dns"
|
|
|
188
188
|
Requires-Dist: httpx_retries; extra == "dns"
|
|
189
189
|
Requires-Dist: logfire; extra == "dns"
|
|
190
190
|
Requires-Dist: logfire[httpx]; extra == "dns"
|
|
191
|
+
Requires-Dist: diskcache; extra == "dns"
|
|
192
|
+
Requires-Dist: cachetools; extra == "dns"
|
|
191
193
|
Provides-Extra: patterns
|
|
192
194
|
Requires-Dist: regex; extra == "patterns"
|
|
193
195
|
Provides-Extra: http
|
|
@@ -214,6 +216,9 @@ Requires-Dist: av; extra == "av"
|
|
|
214
216
|
Provides-Extra: env
|
|
215
217
|
Provides-Extra: env-io
|
|
216
218
|
Requires-Dist: dotenv; extra == "env-io"
|
|
219
|
+
Provides-Extra: toml
|
|
220
|
+
Provides-Extra: toml-write
|
|
221
|
+
Requires-Dist: tomlkit; extra == "toml-write"
|
|
217
222
|
Provides-Extra: ha
|
|
218
223
|
Requires-Dist: dotenv; extra == "ha"
|
|
219
224
|
Provides-Extra: ha-api
|
|
@@ -3,6 +3,7 @@ from pydantic_settings import CliSubCommand
|
|
|
3
3
|
from corio import dm
|
|
4
4
|
from corio import sec
|
|
5
5
|
from corio import sets
|
|
6
|
+
from corio.path import Path
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class DocServe(dm.Base):
|
|
@@ -42,6 +43,17 @@ class EpTest(dm.Base):
|
|
|
42
43
|
print("Ran test entrypoint.")
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
class Test(dm.Base):
|
|
47
|
+
name: str = Path.cwd().name
|
|
48
|
+
|
|
49
|
+
def run(self):
|
|
50
|
+
from corio.infra.project import Project
|
|
51
|
+
|
|
52
|
+
project = Project(self.name)
|
|
53
|
+
is_passed = project.releaser.tester.run()
|
|
54
|
+
return int(not is_passed)
|
|
55
|
+
|
|
56
|
+
|
|
45
57
|
class ShellDebug(dm.Base):
|
|
46
58
|
def run(self):
|
|
47
59
|
from corio import debug
|
|
@@ -81,6 +93,7 @@ class Cli(sets.Base, cli_parse_args=True):
|
|
|
81
93
|
secrets: CliSubCommand[sec.Cli]
|
|
82
94
|
docs: CliSubCommand[Docs]
|
|
83
95
|
pyproject: CliSubCommand[Pyproject]
|
|
96
|
+
test: CliSubCommand[Test]
|
|
84
97
|
ep_test: CliSubCommand[EpTest]
|
|
85
98
|
shell_debug: CliSubCommand[ShellDebug]
|
|
86
99
|
cache_hfh: CliSubCommand[CacheHfh]
|
|
@@ -584,13 +584,7 @@ class Tester(Inherit[Releaser]):
|
|
|
584
584
|
@cached_property
|
|
585
585
|
def dependencies(self) -> dict[str, list[str]]:
|
|
586
586
|
data = self.paths.pyproject_repo.read_toml()
|
|
587
|
-
|
|
588
|
-
dependencies = {
|
|
589
|
-
str(key): [str(value) for value in values]
|
|
590
|
-
for key, values in table.items()
|
|
591
|
-
if isinstance(values, list)
|
|
592
|
-
}
|
|
593
|
-
return dependencies
|
|
587
|
+
return data["project"].get("optional-dependencies",{})
|
|
594
588
|
|
|
595
589
|
@cached_property
|
|
596
590
|
def modules(self) -> list[str]:
|
|
@@ -608,9 +602,11 @@ class Tester(Inherit[Releaser]):
|
|
|
608
602
|
def get_env(self, name: str, path_tests: Path, extras: list[str]) -> dict:
|
|
609
603
|
if path_tests.is_relative_to(self.paths.repo):
|
|
610
604
|
path_tests = path_tests.relative_to(self.paths.repo)
|
|
605
|
+
deps = self.get_deps_extras(extras)
|
|
611
606
|
env = {
|
|
612
607
|
"description": f"Run {name} tests.",
|
|
613
608
|
"extras": extras,
|
|
609
|
+
"deps": deps,
|
|
614
610
|
"commands": [["python", "-m", "pytest", "-q", str(path_tests)]],
|
|
615
611
|
}
|
|
616
612
|
return env
|
|
@@ -619,6 +615,22 @@ class Tester(Inherit[Releaser]):
|
|
|
619
615
|
def env(self) -> dict:
|
|
620
616
|
return self.get_env(name=self.paths.name_ns, path_tests=self.paths.tests, extras=["test"])
|
|
621
617
|
|
|
618
|
+
def get_extras_module(self, module: str) -> list[str]:
|
|
619
|
+
extras = ["test"]
|
|
620
|
+
extras_available = set(self.dependencies.keys())
|
|
621
|
+
|
|
622
|
+
if module in extras_available:
|
|
623
|
+
extras.insert(0, module)
|
|
624
|
+
|
|
625
|
+
extras_children = sorted(extra for extra in extras_available if extra.startswith(f"{module}."))
|
|
626
|
+
return extras_children + extras
|
|
627
|
+
|
|
628
|
+
def get_deps_extras(self, extras: list[str]) -> list[str]:
|
|
629
|
+
resolved = []
|
|
630
|
+
for extra in extras:
|
|
631
|
+
resolved.extend(self.dependencies.get(extra, []))
|
|
632
|
+
return list(dict.fromkeys(resolved))
|
|
633
|
+
|
|
622
634
|
@cached_property
|
|
623
635
|
def envs(self) -> dict[str, dict]:
|
|
624
636
|
if not self.paths.metadata.test_envs:
|
|
@@ -627,12 +639,9 @@ class Tester(Inherit[Releaser]):
|
|
|
627
639
|
return {self.paths.name_ns: self.env}
|
|
628
640
|
|
|
629
641
|
envs = {}
|
|
630
|
-
extras_available = set(self.dependencies.keys())
|
|
631
642
|
|
|
632
643
|
for module in self.modules:
|
|
633
|
-
extras =
|
|
634
|
-
if module in extras_available:
|
|
635
|
-
extras.insert(0, module)
|
|
644
|
+
extras = self.get_extras_module(module)
|
|
636
645
|
|
|
637
646
|
path_test = self.paths.tests / f"{self.TEST_FILENAME_PREFIX}{module}{self.TEST_FILENAME_SUFFIX}"
|
|
638
647
|
name = f"{self.paths.name_ns}.{module}"
|
|
@@ -40,7 +40,7 @@ debug = ["pydevd-pycharm~=251.25410.159"]
|
|
|
40
40
|
sets = ["pydantic-settings", "dm", "yaml"]
|
|
41
41
|
"path.app" = ["appdirs"]
|
|
42
42
|
"path.type" = ["filetype"]
|
|
43
|
-
dns = ["dnspython[doh]", "http"]
|
|
43
|
+
dns = ["dnspython[doh]", "http", "caching"]
|
|
44
44
|
patterns = ["regex"]
|
|
45
45
|
http = ["httpx", "httpx_retries", "logging", "logfire[httpx]"]
|
|
46
46
|
setup = ["setuptools"]
|
|
@@ -52,11 +52,13 @@ mqtt = ["aiomqtt"]
|
|
|
52
52
|
av = ["av"]
|
|
53
53
|
env = []
|
|
54
54
|
"env.io" = ["dotenv"]
|
|
55
|
+
toml = []
|
|
56
|
+
"toml.write" = ["tomlkit"]
|
|
55
57
|
ha = ["env.io"]
|
|
56
58
|
"ha.api" = ["ha", "homeassistant_api", "aiohasupervisor"]
|
|
57
59
|
doc = ["mkdocs", "mkdocs-material", "mkdocstrings[python]", "mike", "mkdocs-include-dir-to-nav"]
|
|
58
60
|
youtube = ["pytubefix"]
|
|
59
|
-
infra = ["version.dev", "logging", "setup", "doc", "sets", "build", "twine", "packaging", "vcs", "docker.client", "merging", "http", "api", "
|
|
61
|
+
infra = ["version.dev", "logging", "setup", "doc", "sets", "build", "twine", "packaging", "vcs", "docker.client", "merging", "http", "api", "toml.write", "secrets", "cli", "test"]
|
|
60
62
|
vcs = ["pygit2"]
|
|
61
63
|
tasmota = ["decode-config", "esptool"]
|
|
62
64
|
encrypt = ["pyrage"]
|
|
@@ -64,7 +66,7 @@ secrets = ["encrypt", "env.io", "yaml", "logging", "sets", "vcs"]
|
|
|
64
66
|
cli = ["sets", "logging"]
|
|
65
67
|
|
|
66
68
|
[tool.corio.metadata]
|
|
67
|
-
version = "2.2.
|
|
69
|
+
version = "2.2.4"
|
|
68
70
|
port = 0
|
|
69
71
|
base = "python"
|
|
70
72
|
description = "Collection of high-level tools to simplify everyday development tasks, with a focus on AI/ML"
|
|
@@ -94,7 +96,7 @@ corio = ["pyproject.package.toml"]
|
|
|
94
96
|
|
|
95
97
|
[project]
|
|
96
98
|
name = "corio"
|
|
97
|
-
version = "2.2.
|
|
99
|
+
version = "2.2.4"
|
|
98
100
|
description = "Collection of high-level tools to simplify everyday development tasks, with a focus on AI/ML"
|
|
99
101
|
readme = "README.md"
|
|
100
102
|
dependencies = []
|
|
@@ -139,7 +141,7 @@ debug = ["pydevd-pycharm~=251.25410.159"]
|
|
|
139
141
|
sets = ["pydantic-settings", "pydantic", "pydantic-extra-types", "pycountry", "yamlscript", "pyyaml"]
|
|
140
142
|
"path.app" = ["appdirs"]
|
|
141
143
|
"path.type" = ["filetype"]
|
|
142
|
-
dns = ["dnspython[doh]", "httpx", "httpx_retries", "logfire", "logfire[httpx]"]
|
|
144
|
+
dns = ["dnspython[doh]", "httpx", "httpx_retries", "logfire", "logfire[httpx]", "diskcache", "cachetools"]
|
|
143
145
|
patterns = ["regex"]
|
|
144
146
|
http = ["httpx", "httpx_retries", "logfire", "logfire[httpx]"]
|
|
145
147
|
setup = ["setuptools"]
|
|
@@ -151,6 +153,8 @@ mqtt = ["aiomqtt"]
|
|
|
151
153
|
av = ["av"]
|
|
152
154
|
env = []
|
|
153
155
|
"env.io" = ["dotenv"]
|
|
156
|
+
toml = []
|
|
157
|
+
"toml.write" = ["tomlkit"]
|
|
154
158
|
ha = ["dotenv"]
|
|
155
159
|
"ha.api" = ["dotenv", "homeassistant_api", "aiohasupervisor"]
|
|
156
160
|
doc = ["mkdocs", "mkdocs-material", "mkdocstrings[python]", "mike", "mkdocs-include-dir-to-nav"]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import dns
|
|
2
|
+
from dns import rcode as dnspython_rcode
|
|
3
|
+
|
|
4
|
+
from corio.dns import client as dns_client
|
|
5
|
+
from corio.dns import dm as dns_dm
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _make_exchange(name: str = "example.com.", rdtype=dns.rdatatype.A) -> dns_dm.Exchange:
|
|
9
|
+
query = dns.message.make_query(name, rdtype)
|
|
10
|
+
return dns_dm.Exchange.from_wire(query.to_wire(), ip="127.0.0.1", port=5353)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_response_ttl_from_answers_authority_and_rcode_defaults():
|
|
14
|
+
exchange = _make_exchange()
|
|
15
|
+
message = exchange.request.get_response_template()
|
|
16
|
+
message.answer.append(dns.rrset.from_text("example.com.", 300, "IN", "A", "1.1.1.1"))
|
|
17
|
+
message.answer.append(dns.rrset.from_text("example.com.", 120, "IN", "A", "1.1.1.2"))
|
|
18
|
+
response = dns_dm.Response.from_message(message)
|
|
19
|
+
assert response.ttl == 120
|
|
20
|
+
|
|
21
|
+
message = exchange.request.get_response_template()
|
|
22
|
+
message.authority.append(dns.rrset.from_text("example.com.", 42, "IN", "NS", "ns1.example.com."))
|
|
23
|
+
response = dns_dm.Response.from_message(message)
|
|
24
|
+
assert response.ttl == 42
|
|
25
|
+
|
|
26
|
+
message = exchange.request.get_response_template()
|
|
27
|
+
message.set_rcode(dnspython_rcode.SERVFAIL)
|
|
28
|
+
response = dns_dm.Response.from_message(message)
|
|
29
|
+
assert response.ttl == 10
|
|
30
|
+
|
|
31
|
+
response = dns_dm.Response.from_message(message, ttl_defaults={"SERVFAIL": 77})
|
|
32
|
+
assert response.ttl == 77
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_exchange_question_last_and_query_last_use_latest_answer_name():
|
|
36
|
+
exchange = _make_exchange(name="example.com.")
|
|
37
|
+
message = exchange.request.get_response_template()
|
|
38
|
+
message.answer.append(dns.rrset.from_text("edge.example.com.", 60, "IN", "A", "9.9.9.9"))
|
|
39
|
+
exchange.response = dns_dm.Response.from_message(message)
|
|
40
|
+
|
|
41
|
+
question_last = exchange.question_last
|
|
42
|
+
assert question_last.name.to_text() == "9.9.9.9"
|
|
43
|
+
assert question_last.rdtype == exchange.request.type
|
|
44
|
+
|
|
45
|
+
query_last = exchange.query_last
|
|
46
|
+
assert query_last.question[0].name.to_text() == "9.9.9.9"
|
|
47
|
+
assert query_last.id == exchange.request.message.id
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_exchange_reverse_builds_internal_ptr_query():
|
|
51
|
+
exchange = _make_exchange()
|
|
52
|
+
reverse = exchange.reverse
|
|
53
|
+
|
|
54
|
+
assert reverse.is_internal is True
|
|
55
|
+
assert reverse.ip == exchange.ip
|
|
56
|
+
assert reverse.port == exchange.port
|
|
57
|
+
assert reverse.request.type_text == "PTR"
|
|
58
|
+
assert reverse.request.name_text.endswith(".in-addr.arpa.")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_plain_client_resolve_applies_ttl_min(monkeypatch):
|
|
62
|
+
exchange = _make_exchange()
|
|
63
|
+
upstream = exchange.request.get_response_template()
|
|
64
|
+
upstream.answer.append(dns.rrset.from_text("example.com.", 3, "IN", "A", "1.1.1.1"))
|
|
65
|
+
|
|
66
|
+
monkeypatch.setattr(dns_client.dnspython_query, "udp", lambda q, where, port: upstream)
|
|
67
|
+
|
|
68
|
+
client_plain = dns_client.Plain(host="8.8.8.8", ttl_min=30)
|
|
69
|
+
client_plain.resolve(exchange)
|
|
70
|
+
|
|
71
|
+
assert exchange.response.answer is not None
|
|
72
|
+
assert exchange.response.answer.ttl == 30
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_http_client_resolve_sets_servfail_on_exception(monkeypatch):
|
|
76
|
+
exchange = _make_exchange()
|
|
77
|
+
client_http = dns_client.HTTP(host="dns.google", url="https://{host}/dns-query")
|
|
78
|
+
client_http.__dict__["ip"] = "1.1.1.1"
|
|
79
|
+
|
|
80
|
+
def _raise(*_args, **_kwargs):
|
|
81
|
+
raise RuntimeError("boom")
|
|
82
|
+
|
|
83
|
+
monkeypatch.setattr(dns_client, "logger", type("L", (), {"exception": staticmethod(lambda *_args, **_kwargs: None)})())
|
|
84
|
+
monkeypatch.setattr(client_http.CLIENT, "post", _raise)
|
|
85
|
+
|
|
86
|
+
client_http.resolve(exchange)
|
|
87
|
+
|
|
88
|
+
assert exchange.response.rcode == dnspython_rcode.SERVFAIL
|
|
89
|
+
assert exchange.response.is_complete is True
|
|
@@ -73,3 +73,13 @@ def test_get_env_dict():
|
|
|
73
73
|
with helpers.patch_environment(clear=True, **ENVIRONMENT_DATA):
|
|
74
74
|
actual = env.get_dict()
|
|
75
75
|
assert actual == expected
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_env_file_round_trip_via_path_data(tmp_path):
|
|
79
|
+
path_env = Path(tmp_path / ".env")
|
|
80
|
+
expected = {"A": "1", "B": "two"}
|
|
81
|
+
|
|
82
|
+
path_env.write_data(expected)
|
|
83
|
+
actual = path_env.read_data()
|
|
84
|
+
|
|
85
|
+
assert actual == expected
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from corio import hash as hash_module
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_hash_unit_is_stable_and_bounded():
|
|
5
|
+
first = hash_module.hash_unit("corio")
|
|
6
|
+
second = hash_module.hash_unit("corio")
|
|
7
|
+
|
|
8
|
+
assert first == second
|
|
9
|
+
assert 0.0 <= first < 1.0
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_get_hash_readable_length_and_replacements():
|
|
13
|
+
value = hash_module.get_hash_readable("corio", length=16)
|
|
14
|
+
|
|
15
|
+
assert len(value) == 16
|
|
16
|
+
assert "O" not in value
|
|
17
|
+
assert "I" not in value
|
|
18
|
+
assert "=" not in value
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from corio.hook import ImportHook, MissingExtraError, MissingExtraMockModule
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_missing_extra_mock_module_raises_with_context():
|
|
7
|
+
mock_module = MissingExtraMockModule("path.app", ModuleNotFoundError("no appdirs"))
|
|
8
|
+
|
|
9
|
+
with pytest.raises(MissingExtraError):
|
|
10
|
+
mock_module()
|
|
11
|
+
|
|
12
|
+
with pytest.raises(MissingExtraError):
|
|
13
|
+
_ = mock_module.any_attr
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_import_hook_translates_module_not_found_for_corio_callers():
|
|
17
|
+
hook = ImportHook(auto_register=False)
|
|
18
|
+
|
|
19
|
+
def fake_import(*_args, **_kwargs):
|
|
20
|
+
raise ModuleNotFoundError("missing dep")
|
|
21
|
+
|
|
22
|
+
hook._previous_import = fake_import
|
|
23
|
+
|
|
24
|
+
with pytest.raises(MissingExtraError):
|
|
25
|
+
hook("missing", globals={"__name__": "corio.path.app"})
|
|
26
|
+
|
|
27
|
+
with pytest.raises(ModuleNotFoundError):
|
|
28
|
+
hook("missing", globals={"__name__": "external.module"})
|
|
@@ -3,6 +3,7 @@ from types import SimpleNamespace
|
|
|
3
3
|
from packaging.requirements import Requirement
|
|
4
4
|
|
|
5
5
|
from corio.infra.incrementor_pyproject import IncrementorPyproject
|
|
6
|
+
from corio.infra.releaser import Tester as ReleaserTester
|
|
6
7
|
from corio.path import Path
|
|
7
8
|
|
|
8
9
|
|
|
@@ -160,3 +161,88 @@ def test_process_deps_pins_project_dependencies(tmp_path):
|
|
|
160
161
|
|
|
161
162
|
assert incrementor._process_deps(dependencies) == ["haco==1.2.3", "requests>=2"]
|
|
162
163
|
assert incrementor._process_deps(optional_dev) == ["haco[logging]==1.2.3", "pytest"]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _make_tester(path_repo: Path, *, test_envs: bool) -> ReleaserTester:
|
|
167
|
+
path_tests = path_repo / "corio" / "tests"
|
|
168
|
+
path_tests.mkdir(parents=True)
|
|
169
|
+
path_pyproject = path_repo / "pyproject.toml"
|
|
170
|
+
path_pyproject.write_text(
|
|
171
|
+
"\n".join(
|
|
172
|
+
[
|
|
173
|
+
"[project]",
|
|
174
|
+
'name = "corio"',
|
|
175
|
+
'version = "0.0.0"',
|
|
176
|
+
"",
|
|
177
|
+
"[project.optional-dependencies]",
|
|
178
|
+
'test = ["pytest", "pytest-cov"]',
|
|
179
|
+
'path = []',
|
|
180
|
+
'"path.app" = ["appdirs"]',
|
|
181
|
+
'"path.type" = ["filetype"]',
|
|
182
|
+
"strings = []",
|
|
183
|
+
"",
|
|
184
|
+
]
|
|
185
|
+
),
|
|
186
|
+
encoding="utf-8",
|
|
187
|
+
)
|
|
188
|
+
parent = SimpleNamespace(
|
|
189
|
+
name="corio",
|
|
190
|
+
paths=SimpleNamespace(
|
|
191
|
+
repo=path_repo,
|
|
192
|
+
tests=path_tests,
|
|
193
|
+
pyproject_repo=path_pyproject,
|
|
194
|
+
name_ns="corio",
|
|
195
|
+
metadata=SimpleNamespace(test_envs=test_envs),
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
return ReleaserTester(parent)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def test_tester_get_extras_module_merges_module_and_dotted_children(tmp_path):
|
|
202
|
+
path_repo = Path(tmp_path / "corio")
|
|
203
|
+
path_repo.mkdir(parents=True)
|
|
204
|
+
tester = _make_tester(path_repo, test_envs=True)
|
|
205
|
+
|
|
206
|
+
assert tester.get_extras_module("path") == ["path.app", "path.type", "path", "test"]
|
|
207
|
+
assert tester.get_extras_module("strings") == ["strings", "test"]
|
|
208
|
+
assert tester.get_extras_module("missing") == ["test"]
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_tester_get_deps_extras_resolves_recursive_dependencies(tmp_path):
|
|
212
|
+
path_repo = Path(tmp_path / "corio")
|
|
213
|
+
path_repo.mkdir(parents=True)
|
|
214
|
+
tester = _make_tester(path_repo, test_envs=True)
|
|
215
|
+
|
|
216
|
+
deps = tester.get_deps_extras(["path.app", "path.type", "test"])
|
|
217
|
+
assert "pytest" in deps
|
|
218
|
+
assert "pytest-cov" in deps
|
|
219
|
+
|
|
220
|
+
deps_missing = tester.get_deps_extras(["missing"])
|
|
221
|
+
assert deps_missing == []
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def test_tester_envs_use_file_module_name_with_test_envs(tmp_path):
|
|
225
|
+
path_repo = Path(tmp_path / "corio")
|
|
226
|
+
path_repo.mkdir(parents=True)
|
|
227
|
+
tester = _make_tester(path_repo, test_envs=True)
|
|
228
|
+
(tester.paths.tests / "test_path.py").write_text("", encoding="utf-8")
|
|
229
|
+
(tester.paths.tests / "test_strings.py").write_text("", encoding="utf-8")
|
|
230
|
+
|
|
231
|
+
envs = tester.envs
|
|
232
|
+
|
|
233
|
+
assert set(envs) == {"corio.path", "corio.strings"}
|
|
234
|
+
assert envs["corio.path"]["extras"] == ["path.app", "path.type", "path", "test"]
|
|
235
|
+
assert envs["corio.strings"]["extras"] == ["strings", "test"]
|
|
236
|
+
assert "pytest" in envs["corio.path"]["deps"]
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_tester_envs_fall_back_to_single_env_when_test_envs_disabled(tmp_path):
|
|
240
|
+
path_repo = Path(tmp_path / "corio")
|
|
241
|
+
path_repo.mkdir(parents=True)
|
|
242
|
+
tester = _make_tester(path_repo, test_envs=False)
|
|
243
|
+
(tester.paths.tests / "test_path.py").write_text("", encoding="utf-8")
|
|
244
|
+
|
|
245
|
+
envs = tester.envs
|
|
246
|
+
|
|
247
|
+
assert set(envs) == {"corio"}
|
|
248
|
+
assert envs["corio"]["extras"] == ["test"]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from corio import iterator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_enlist_and_dedupe():
|
|
7
|
+
assert iterator.enlist("x") == ["x"]
|
|
8
|
+
assert iterator.enlist(["x"]) == ["x"]
|
|
9
|
+
assert iterator.dedupe(["a", "b", "a", "c"]) == ["a", "b", "c"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_dict_records_to_lists_and_chunking():
|
|
13
|
+
data = [{"a": 1}, {"b": 2}]
|
|
14
|
+
as_lists = iterator.dict_records_to_lists(data, missing=None)
|
|
15
|
+
assert as_lists == {"a": [1, None], "b": [None, 2]}
|
|
16
|
+
|
|
17
|
+
assert iterator.chunk_data([1, 2, 3, 4, 5], size=2) == [[1, 2], [3, 4], [5]]
|
|
18
|
+
assert iterator.get_batch_sizes(total=10, num_batches=3) == [4, 3, 3]
|
|
19
|
+
assert list(iterator.rebatch([[1, 2], [3], [4, 5]], size=2)) == [(1, 2), (3, 4), (5,)]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_strip_none_flatten_tree_and_iterdiffer():
|
|
23
|
+
assert iterator.strip_none(1, None, 2) == [1, 2]
|
|
24
|
+
|
|
25
|
+
tree = {"a": {"b": 1}, "list": [2, 3]}
|
|
26
|
+
flat = iterator.flatten_tree(tree, sep=".")
|
|
27
|
+
assert flat["a.b"] == 1
|
|
28
|
+
assert flat["list.[0]"] == 2
|
|
29
|
+
assert flat["list.[1]"] == 3
|
|
30
|
+
|
|
31
|
+
diff = iterator.IterDiffer(before=[1, 2], after=[2, 3])
|
|
32
|
+
assert diff.added == {3}
|
|
33
|
+
assert diff.removed == {1}
|
|
34
|
+
assert diff.is_changed is True
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class _Obj:
|
|
39
|
+
key: str
|
|
40
|
+
value: int
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_index_list_lookup_by_attr_and_key():
|
|
44
|
+
objects = iterator.IndexList([_Obj(key="a", value=1), _Obj(key="b", value=2)])
|
|
45
|
+
assert objects.key["a"].value == 1
|
|
46
|
+
|
|
47
|
+
dicts = iterator.IndexList([{"id": "x", "value": 1}, {"id": "y", "value": 2}])
|
|
48
|
+
assert dicts.id["y"]["value"] == 2
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from corio import jsn
|
|
2
|
+
from corio.path import Path
|
|
3
|
+
from corio.tests.helpers import SERIALIZATION_DATA
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_json():
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
Simple YAML round trip test
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
expected = SERIALIZATION_DATA
|
|
13
|
+
actual = jsn.from_json(jsn.to_json(expected))
|
|
14
|
+
assert actual == expected
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_json_path_round_trip(tmp_path):
|
|
18
|
+
expected = SERIALIZATION_DATA
|
|
19
|
+
path_json = Path(tmp_path / "serialization_test.json")
|
|
20
|
+
|
|
21
|
+
path_json.write_json(expected)
|
|
22
|
+
actual = path_json.read_json()
|
|
23
|
+
|
|
24
|
+
assert actual == expected
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
from corio import name
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_name_lists_are_non_empty():
|
|
7
|
+
assert len(name.get_left()) > 0
|
|
8
|
+
assert len(name.get_right()) > 0
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_get_name_tuple_or_string():
|
|
12
|
+
random.seed(0)
|
|
13
|
+
left_right = name.get(sep=None)
|
|
14
|
+
assert isinstance(left_right, tuple)
|
|
15
|
+
assert len(left_right) == 2
|
|
16
|
+
|
|
17
|
+
random.seed(0)
|
|
18
|
+
text = name.get(sep="-")
|
|
19
|
+
assert isinstance(text, str)
|
|
20
|
+
assert "-" in text
|