daktari 0.0.298__py3-none-any.whl → 0.0.303__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.
daktari/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.0.298"
1
+ __version__ = "0.0.303"
@@ -0,0 +1,136 @@
1
+ import logging
2
+ import os
3
+ from dataclasses import dataclass
4
+ from typing import List, Optional
5
+ from urllib.parse import urlparse
6
+
7
+ from daktari.check import Check, CheckResult
8
+ from daktari.command_utils import can_run_command
9
+ from daktari.file_utils import file_exists
10
+ from daktari.os import OS
11
+
12
+
13
+ @dataclass
14
+ class NpmrcScope:
15
+ name: str
16
+ registry: str
17
+ requireAuthToken: bool = False
18
+
19
+
20
+ def get_npmrc_path() -> str:
21
+ return os.path.expanduser("~/.npmrc")
22
+
23
+
24
+ def get_npmrc_contents() -> List[str]:
25
+ npmrc_path = get_npmrc_path()
26
+ if not file_exists(npmrc_path):
27
+ raise Exception(f"{npmrc_path} does not exist")
28
+
29
+ try:
30
+ with open(npmrc_path, "r") as npmrc_file:
31
+ return npmrc_file.readlines()
32
+ except Exception:
33
+ logging.error(f"Exception reading {npmrc_path}", exc_info=True)
34
+ raise Exception("Failed to read npmrc")
35
+
36
+
37
+ def get_registry_host(registry_url: str) -> str:
38
+ parsed = urlparse(registry_url)
39
+ return parsed.netloc
40
+
41
+
42
+ def npmrc_contains_scope_registry(lines: List[str], scope: NpmrcScope) -> bool:
43
+ expected_line = f"@{scope.name}:registry={scope.registry}"
44
+ for line in lines:
45
+ stripped = line.strip()
46
+ if stripped == expected_line or stripped.startswith(expected_line + "\n"):
47
+ return True
48
+ return False
49
+
50
+
51
+ def npmrc_contains_auth_token(lines: List[str], registry_url: str) -> bool:
52
+ host = get_registry_host(registry_url)
53
+ auth_token_prefix = f"//{host}/:_authToken="
54
+ for line in lines:
55
+ stripped = line.strip()
56
+ if stripped.startswith(auth_token_prefix):
57
+ token_value = stripped[len(auth_token_prefix) :]
58
+ if token_value and token_value != "UPDATE_WITH_TOKEN":
59
+ return True
60
+ return False
61
+
62
+
63
+ def npmrc_scope_is_configured(lines: List[str], scope: NpmrcScope) -> bool:
64
+ if not npmrc_contains_scope_registry(lines, scope):
65
+ return False
66
+ if scope.requireAuthToken and not npmrc_contains_auth_token(lines, scope.registry):
67
+ return False
68
+ return True
69
+
70
+
71
+ def get_npmrc_suggestion(scope: NpmrcScope) -> str:
72
+ lines = [f"@{scope.name}:registry={scope.registry}"]
73
+ if scope.requireAuthToken:
74
+ host = get_registry_host(scope.registry)
75
+ lines.append(f"//{host}/:_authToken=UPDATE_WITH_TOKEN")
76
+ return "\n".join(lines)
77
+
78
+
79
+ def get_npmrc_token_for_registry(registry_url: str) -> Optional[str]:
80
+ lines = get_npmrc_contents()
81
+ host = get_registry_host(registry_url)
82
+ auth_token_prefix = f"//{host}/:_authToken="
83
+ for line in lines:
84
+ stripped = line.strip()
85
+ if stripped.startswith(auth_token_prefix):
86
+ return stripped[len(auth_token_prefix) :]
87
+ return None
88
+
89
+
90
+ class NpmrcScopeConfigured(Check):
91
+ name = "npmrc.scopeConfigured"
92
+
93
+ def __init__(self, scope: NpmrcScope, tokenInstructions: Optional[str] = None):
94
+ self.scope = scope
95
+ self.npmrc_suggestion = get_npmrc_suggestion(scope)
96
+ tokenInstructionString = f"\n\n{tokenInstructions}" if tokenInstructions else ""
97
+ self.suggestions = {
98
+ OS.GENERIC: f"""Add the lines below to ~/.npmrc:
99
+
100
+ {self.npmrc_suggestion}{tokenInstructionString}"""
101
+ }
102
+
103
+ def check(self) -> CheckResult:
104
+ try:
105
+ lines = get_npmrc_contents()
106
+ except Exception as e:
107
+ return self.failed(str(e))
108
+
109
+ if not npmrc_scope_is_configured(lines, self.scope):
110
+ return self.failed(f"Scope {self.scope.name} not configured in npmrc")
111
+
112
+ return self.passed(f"Scope {self.scope.name} configured in npmrc")
113
+
114
+
115
+ class NpmrcGithubTokenValid(Check):
116
+ name = "npmrc.githubTokenValid"
117
+ depends_on = [NpmrcScopeConfigured]
118
+
119
+ def __init__(self, scope_name: str, test_package: str):
120
+ self.scope_name = scope_name
121
+ self.test_package = test_package
122
+ self.suggestions = {
123
+ OS.GENERIC: "Please check the token was copied correctly from GitHub."
124
+ " Ensure the token hasn't expired, or has been revoked."
125
+ " Also, ensure it has the correct permissions to read packages."
126
+ }
127
+
128
+ def check(self) -> CheckResult:
129
+ package_spec = f"@{self.scope_name}/{self.test_package}"
130
+ command = f"npm view {package_spec} version"
131
+ logging.debug(f"Checking npm registry access with: {command}")
132
+
133
+ if can_run_command(command):
134
+ return self.passed(f"GitHub npm token is valid (can access {package_spec})")
135
+ else:
136
+ return self.failed(f"GitHub npm token is not valid (cannot access {package_spec})")
daktari/checks/pnpm.py ADDED
@@ -0,0 +1,14 @@
1
+ from daktari.check import Check, CheckResult
2
+ from daktari.os import OS
3
+
4
+
5
+ class PnpmInstalled(Check):
6
+ name = "pnpm.installed"
7
+
8
+ suggestions = {
9
+ OS.OS_X: "<cmd>brew install pnpm</cmd>",
10
+ OS.GENERIC: "<cmd>npm install -g pnpm</cmd>",
11
+ }
12
+
13
+ def check(self) -> CheckResult:
14
+ return self.verify_install("pnpm")
@@ -0,0 +1,161 @@
1
+ import unittest
2
+ from unittest import mock
3
+
4
+ from daktari.check import CheckStatus
5
+ from daktari.checks.npmrc import (
6
+ NpmrcScope,
7
+ NpmrcScopeConfigured,
8
+ NpmrcGithubTokenValid,
9
+ get_registry_host,
10
+ npmrc_contains_scope_registry,
11
+ npmrc_contains_auth_token,
12
+ npmrc_scope_is_configured,
13
+ get_npmrc_suggestion,
14
+ )
15
+
16
+ TEST_SCOPE_NAME = "my-org"
17
+ TEST_REGISTRY = "https://npm.pkg.github.com"
18
+ TEST_REGISTRY_HOST = "npm.pkg.github.com"
19
+ TEST_AUTH_TOKEN = "ghp_abc123xyz"
20
+
21
+
22
+ class TestNpmrcHelpers(unittest.TestCase):
23
+ def test_get_registry_host(self):
24
+ self.assertEqual(get_registry_host("https://npm.pkg.github.com"), "npm.pkg.github.com")
25
+ self.assertEqual(get_registry_host("https://registry.npmjs.org"), "registry.npmjs.org")
26
+ self.assertEqual(get_registry_host("https://npm.example.com/custom/path"), "npm.example.com")
27
+
28
+ def test_npmrc_contains_scope_registry_matching(self):
29
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
30
+ lines = [
31
+ f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
32
+ f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
33
+ ]
34
+ self.assertTrue(npmrc_contains_scope_registry(lines, scope))
35
+
36
+ def test_npmrc_contains_scope_registry_not_matching(self):
37
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
38
+ lines = [
39
+ "@other-scope:registry=https://registry.npmjs.org\n",
40
+ ]
41
+ self.assertFalse(npmrc_contains_scope_registry(lines, scope))
42
+
43
+ def test_npmrc_contains_scope_registry_empty_lines(self):
44
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
45
+ lines = []
46
+ self.assertFalse(npmrc_contains_scope_registry(lines, scope))
47
+
48
+ def test_npmrc_contains_auth_token_matching(self):
49
+ lines = [
50
+ f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
51
+ f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
52
+ ]
53
+ self.assertTrue(npmrc_contains_auth_token(lines, TEST_REGISTRY))
54
+
55
+ def test_npmrc_contains_auth_token_not_matching(self):
56
+ lines = [
57
+ f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
58
+ "//registry.npmjs.org/:_authToken=some-token\n",
59
+ ]
60
+ self.assertFalse(npmrc_contains_auth_token(lines, TEST_REGISTRY))
61
+
62
+ def test_npmrc_contains_auth_token_placeholder_rejected(self):
63
+ lines = [
64
+ f"//{TEST_REGISTRY_HOST}/:_authToken=UPDATE_WITH_TOKEN\n",
65
+ ]
66
+ self.assertFalse(npmrc_contains_auth_token(lines, TEST_REGISTRY))
67
+
68
+ def test_npmrc_contains_auth_token_empty_rejected(self):
69
+ lines = [
70
+ f"//{TEST_REGISTRY_HOST}/:_authToken=\n",
71
+ ]
72
+ self.assertFalse(npmrc_contains_auth_token(lines, TEST_REGISTRY))
73
+
74
+ def test_npmrc_scope_is_configured_without_auth(self):
75
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, requireAuthToken=False)
76
+ lines = [
77
+ f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
78
+ ]
79
+ self.assertTrue(npmrc_scope_is_configured(lines, scope))
80
+
81
+ def test_npmrc_scope_is_configured_with_auth_present(self):
82
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, requireAuthToken=True)
83
+ lines = [
84
+ f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
85
+ f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
86
+ ]
87
+ self.assertTrue(npmrc_scope_is_configured(lines, scope))
88
+
89
+ def test_npmrc_scope_is_configured_with_auth_missing(self):
90
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, requireAuthToken=True)
91
+ lines = [
92
+ f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
93
+ ]
94
+ self.assertFalse(npmrc_scope_is_configured(lines, scope))
95
+
96
+ def test_npmrc_scope_is_configured_registry_missing(self):
97
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
98
+ lines = [
99
+ f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
100
+ ]
101
+ self.assertFalse(npmrc_scope_is_configured(lines, scope))
102
+
103
+ def test_get_npmrc_suggestion_without_auth(self):
104
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, requireAuthToken=False)
105
+ suggestion = get_npmrc_suggestion(scope)
106
+ self.assertEqual(suggestion, f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}")
107
+
108
+ def test_get_npmrc_suggestion_with_auth(self):
109
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, requireAuthToken=True)
110
+ suggestion = get_npmrc_suggestion(scope)
111
+ expected = f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n//{TEST_REGISTRY_HOST}/:_authToken=UPDATE_WITH_TOKEN"
112
+ self.assertEqual(suggestion, expected)
113
+
114
+
115
+ class TestNpmrcScopeConfigured(unittest.TestCase):
116
+ @mock.patch("daktari.checks.npmrc.get_npmrc_contents")
117
+ def test_returns_pass_when_scope_configured(self, mock_get_contents):
118
+ mock_get_contents.return_value = [
119
+ f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
120
+ f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
121
+ ]
122
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, requireAuthToken=True)
123
+ result = NpmrcScopeConfigured(scope).check()
124
+ self.assertEqual(result.status, CheckStatus.PASS)
125
+
126
+ @mock.patch("daktari.checks.npmrc.get_npmrc_contents")
127
+ def test_returns_fail_when_scope_not_configured(self, mock_get_contents):
128
+ mock_get_contents.return_value = [
129
+ "@other-scope:registry=https://registry.npmjs.org\n",
130
+ ]
131
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
132
+ result = NpmrcScopeConfigured(scope).check()
133
+ self.assertEqual(result.status, CheckStatus.FAIL)
134
+
135
+ @mock.patch("daktari.checks.npmrc.get_npmrc_contents")
136
+ def test_returns_fail_when_file_not_found(self, mock_get_contents):
137
+ mock_get_contents.side_effect = Exception("~/.npmrc does not exist")
138
+ scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
139
+ result = NpmrcScopeConfigured(scope).check()
140
+ self.assertEqual(result.status, CheckStatus.FAIL)
141
+ self.assertIn("does not exist", result.summary)
142
+
143
+
144
+ class TestNpmrcGithubTokenValid(unittest.TestCase):
145
+ @mock.patch("daktari.checks.npmrc.can_run_command")
146
+ def test_returns_pass_when_token_valid(self, mock_can_run):
147
+ mock_can_run.return_value = True
148
+ result = NpmrcGithubTokenValid(scope_name=TEST_SCOPE_NAME, test_package="some-package").check()
149
+ self.assertEqual(result.status, CheckStatus.PASS)
150
+ mock_can_run.assert_called_once_with(f"npm view @{TEST_SCOPE_NAME}/some-package version")
151
+
152
+ @mock.patch("daktari.checks.npmrc.can_run_command")
153
+ def test_returns_fail_when_token_invalid(self, mock_can_run):
154
+ mock_can_run.return_value = False
155
+ result = NpmrcGithubTokenValid(scope_name=TEST_SCOPE_NAME, test_package="some-package").check()
156
+ self.assertEqual(result.status, CheckStatus.FAIL)
157
+ mock_can_run.assert_called_once_with(f"npm view @{TEST_SCOPE_NAME}/some-package version")
158
+
159
+
160
+ if __name__ == "__main__":
161
+ unittest.main()
@@ -0,0 +1,25 @@
1
+ import unittest
2
+ from unittest import mock
3
+
4
+ from daktari.check import CheckStatus
5
+ from daktari.checks.pnpm import PnpmInstalled
6
+
7
+
8
+ class TestPnpm(unittest.TestCase):
9
+ @mock.patch("daktari.check.can_run_command")
10
+ def test_pnpm_installed_returns_pass_when_installed(self, mock_can_run_command):
11
+ mock_can_run_command.return_value = True
12
+ result = PnpmInstalled().check()
13
+ self.assertEqual(result.status, CheckStatus.PASS)
14
+ mock_can_run_command.assert_called_once_with("pnpm --version")
15
+
16
+ @mock.patch("daktari.check.can_run_command")
17
+ def test_pnpm_installed_returns_fail_when_not_installed(self, mock_can_run_command):
18
+ mock_can_run_command.return_value = False
19
+ result = PnpmInstalled().check()
20
+ self.assertEqual(result.status, CheckStatus.FAIL)
21
+ mock_can_run_command.assert_called_once_with("pnpm --version")
22
+
23
+
24
+ if __name__ == "__main__":
25
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: daktari
3
- Version: 0.0.298
3
+ Version: 0.0.303
4
4
  Summary: Assist in setting up and maintaining developer environments
5
5
  Author-email: Matt Russell <matthew.russell@sonocent.com>
6
6
  License: Copyright 2021 Sonocent Ltd
@@ -33,7 +33,7 @@ Requires-Dist: dpath==2.2.0
33
33
  Requires-Dist: pyOpenSSL==25.3.0
34
34
  Requires-Dist: types-pyOpenSSL==24.1.0.20240722
35
35
  Requires-Dist: pyclip==0.7.0
36
- Requires-Dist: urllib3==2.5.0
36
+ Requires-Dist: urllib3==2.6.3
37
37
  Dynamic: license-file
38
38
 
39
39
  **Daktari** is a tool to help the initial setup and ongoing maintenance of developer environments. It runs a series of checks (for example, that required software is installed) and provides suggestions on how to fix the issue if the check fails.
@@ -45,7 +45,7 @@ In the root of the project repository, create a `.daktari.py` configuration file
45
45
  ```python
46
46
  from daktari.checks.git import *
47
47
 
48
- version = "0.0.298"
48
+ version = "0.0.303"
49
49
  title = "My Project"
50
50
 
51
51
  checks = [
@@ -1,4 +1,4 @@
1
- daktari/__init__.py,sha256=CH3o5OGlxJBL5e0uaz27Kc-Gfy_rptkWI3Dz896f1gY,24
1
+ daktari/__init__.py,sha256=BDs1YTavXW5cEXxV1GC0G1gQi5n9pQ6qqvxpiQRcIqE,24
2
2
  daktari/__main__.py,sha256=iYwgtZZE2HD9Eh9Io6OliGQwCNZJSde_66jI6kF8xJU,1463
3
3
  daktari/asdf.py,sha256=fALVL6UTz1AYxuabw9MZeAES7J_CvImutUDD1Y2yWwM,509
4
4
  daktari/check.py,sha256=WzIiCrrdH7gCbElNwYbYkjhpmaOs_nUWfvkTBkfZlgs,4133
@@ -41,7 +41,9 @@ daktari/checks/java.py,sha256=vS4gfuLwLxuTLizvybRx2yYXnNLMTsSfmoYHp91rono,3542
41
41
  daktari/checks/kubernetes.py,sha256=BJjxAZzZ3y3t7oHBruUy8OxP41JaX0cxlLvTnFz-F8s,5886
42
42
  daktari/checks/misc.py,sha256=O9_cCkcbigMF7uoqUebDT7mMmmKzQ8EwmIQRBY6vZcc,10855
43
43
  daktari/checks/nodejs.py,sha256=KOCLNQqZqaCalxcIajoDQDom1Zi4_noV4mV_-YZv_4E,2873
44
+ daktari/checks/npmrc.py,sha256=z-hyy4Zevv8q9AE8f5ua54PoSASMk7HLF39Hf2nDWvU,4546
44
45
  daktari/checks/onepassword.py,sha256=C2HWNlypBDTUx4K4IrrEVIkMqivKzX97oE0rENP1xK4,4155
46
+ daktari/checks/pnpm.py,sha256=alEmcc1F-T998ECZ7p7JJ4DaaznnzLwLFz--7mV33Dk,339
45
47
  daktari/checks/python.py,sha256=v6xuRwtFCjzTYMN3qRLu76Xm7Bn8NO7wVpI3d8dOBSc,658
46
48
  daktari/checks/ssh.py,sha256=NqFhO03u523yQaffCeemcRtnYowx6LDuufayzF1t5eQ,1285
47
49
  daktari/checks/terraform.py,sha256=fvA7VRRJftkISqcnEjbc9dUovfXEe0eXrQl2MvsnfSM,2476
@@ -49,7 +51,9 @@ daktari/checks/test_certs.py,sha256=LmwjzNBEJauGSFYYLGevJ5H3FRkaO0piezUm3P-hrbM,
49
51
  daktari/checks/test_intellij_idea.py,sha256=dJ7Nu0eVyv1cpo8h5wUJ6w1rmsbGg3yxslaQKHT3So4,3207
50
52
  daktari/checks/test_java.py,sha256=f-JFGI-J-pBD1WgY8a88BO7XeJhvD87QSZK0kDnflls,3613
51
53
  daktari/checks/test_misc.py,sha256=tTvaPaZORjNf1dV8wPUKXDI3-JksyfdOsnZXFs7SLLE,587
54
+ daktari/checks/test_npmrc.py,sha256=mJG_kO5a7bqWx0NaBRrV6X_4_GKB5-6vU_6uKZQHAmU,7168
52
55
  daktari/checks/test_onepassword.py,sha256=HLeNjkZd85ty1U4Ut3OSjWVa5msC1Fp1OF0IIWD69u4,1115
56
+ daktari/checks/test_pnpm.py,sha256=mZJgQ149V91YtxU4nENtalyyHZBawg4lbtxcW3IoWI0,925
53
57
  daktari/checks/test_ssh.py,sha256=XJECzM9BNODSxfkK71P9TyavlBagzgkCfKWdhlOD7zU,930
54
58
  daktari/checks/test_yarn.py,sha256=9z6NenjJGlWjl5fFQSPVrfiInbvVTxnVhQ-jZkupi6o,4337
55
59
  daktari/checks/xml.py,sha256=BqOAI2QRLYE7QL7lFnk_fdxbLnt6ow5c7rmnLiDbNPo,1019
@@ -57,9 +61,9 @@ daktari/checks/yarn.py,sha256=T8b_oOoZ4l96delmPgPfgtaTkiUS129hoWlGIBcU7Io,5534
57
61
  daktari/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
62
  daktari/resources/daktari-local-template.yaml,sha256=ik7KzMcG1r90nxDOk7t5G7P-BSxQIl2PocGUVe14c44,503
59
63
  daktari/resources/mock_cert.pem,sha256=AIc9dZOVIuxm7KFLunP5WSA1-LDWLOwpfu48B9xQ_Wg,82
60
- daktari-0.0.298.dist-info/licenses/LICENSE.txt,sha256=sMo18UdnQ7rY1SYg9gmop08ubzEQOGO1URYXu1Hxdx0,1051
61
- daktari-0.0.298.dist-info/METADATA,sha256=bBYc40_zfWSvurlyxRYO2rZzMLiLvQHNTReVvbtpXok,4557
62
- daktari-0.0.298.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
63
- daktari-0.0.298.dist-info/entry_points.txt,sha256=mfQXkwKyloAzf7sv78icBo1PtRwf9hCP_816F6REf-I,50
64
- daktari-0.0.298.dist-info/top_level.txt,sha256=LW6kawKAAyxUbGqpAbdedKRUyVy2DBzEZOalp0qDdF8,8
65
- daktari-0.0.298.dist-info/RECORD,,
64
+ daktari-0.0.303.dist-info/licenses/LICENSE.txt,sha256=sMo18UdnQ7rY1SYg9gmop08ubzEQOGO1URYXu1Hxdx0,1051
65
+ daktari-0.0.303.dist-info/METADATA,sha256=CRag-PvQZ0oOqNhrYvrf8RSMV4XzhV4uoq6joGqjkR0,4557
66
+ daktari-0.0.303.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
67
+ daktari-0.0.303.dist-info/entry_points.txt,sha256=mfQXkwKyloAzf7sv78icBo1PtRwf9hCP_816F6REf-I,50
68
+ daktari-0.0.303.dist-info/top_level.txt,sha256=LW6kawKAAyxUbGqpAbdedKRUyVy2DBzEZOalp0qDdF8,8
69
+ daktari-0.0.303.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5