agentpack-cli 0.1.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.
- agentpack/__init__.py +3 -0
- agentpack/adapters/__init__.py +0 -0
- agentpack/adapters/base.py +22 -0
- agentpack/adapters/claude.py +32 -0
- agentpack/adapters/codex.py +26 -0
- agentpack/adapters/cursor.py +29 -0
- agentpack/adapters/generic.py +18 -0
- agentpack/adapters/windsurf.py +26 -0
- agentpack/analysis/__init__.py +0 -0
- agentpack/analysis/dependency_graph.py +80 -0
- agentpack/analysis/go_imports.py +32 -0
- agentpack/analysis/java_imports.py +19 -0
- agentpack/analysis/js_ts_imports.py +53 -0
- agentpack/analysis/python_imports.py +45 -0
- agentpack/analysis/ranking.py +400 -0
- agentpack/analysis/rust_imports.py +32 -0
- agentpack/analysis/symbols.py +154 -0
- agentpack/analysis/tests.py +30 -0
- agentpack/application/__init__.py +0 -0
- agentpack/application/pack_service.py +352 -0
- agentpack/cli.py +33 -0
- agentpack/commands/__init__.py +0 -0
- agentpack/commands/_shared.py +13 -0
- agentpack/commands/benchmark.py +302 -0
- agentpack/commands/claude_cmd.py +55 -0
- agentpack/commands/diff.py +46 -0
- agentpack/commands/doctor.py +185 -0
- agentpack/commands/explain.py +238 -0
- agentpack/commands/init.py +79 -0
- agentpack/commands/install.py +252 -0
- agentpack/commands/monitor.py +105 -0
- agentpack/commands/pack.py +188 -0
- agentpack/commands/scan.py +51 -0
- agentpack/commands/session.py +204 -0
- agentpack/commands/stats.py +138 -0
- agentpack/commands/status.py +37 -0
- agentpack/commands/summarize.py +64 -0
- agentpack/commands/watch.py +185 -0
- agentpack/core/__init__.py +0 -0
- agentpack/core/bootstrap.py +46 -0
- agentpack/core/cache.py +41 -0
- agentpack/core/config.py +101 -0
- agentpack/core/context_pack.py +222 -0
- agentpack/core/diff.py +40 -0
- agentpack/core/git.py +145 -0
- agentpack/core/git_hooks.py +8 -0
- agentpack/core/global_install.py +14 -0
- agentpack/core/ignore.py +66 -0
- agentpack/core/merkle.py +8 -0
- agentpack/core/models.py +115 -0
- agentpack/core/redactor.py +99 -0
- agentpack/core/scanner.py +150 -0
- agentpack/core/snapshot.py +60 -0
- agentpack/core/token_estimator.py +26 -0
- agentpack/core/vscode_tasks.py +5 -0
- agentpack/data/agentpack.md +160 -0
- agentpack/installers/__init__.py +0 -0
- agentpack/installers/claude.py +160 -0
- agentpack/installers/codex.py +54 -0
- agentpack/installers/cursor.py +76 -0
- agentpack/installers/windsurf.py +50 -0
- agentpack/integrations/__init__.py +0 -0
- agentpack/integrations/git_hooks.py +109 -0
- agentpack/integrations/global_install.py +221 -0
- agentpack/integrations/vscode_tasks.py +85 -0
- agentpack/renderers/__init__.py +3 -0
- agentpack/renderers/compact.py +75 -0
- agentpack/renderers/markdown.py +144 -0
- agentpack/renderers/receipts.py +10 -0
- agentpack/session/__init__.py +33 -0
- agentpack/session/state.py +105 -0
- agentpack/summaries/__init__.py +0 -0
- agentpack/summaries/base.py +42 -0
- agentpack/summaries/llm.py +100 -0
- agentpack/summaries/offline.py +97 -0
- agentpack_cli-0.1.0.dist-info/METADATA +1391 -0
- agentpack_cli-0.1.0.dist-info/RECORD +80 -0
- agentpack_cli-0.1.0.dist-info/WHEEL +4 -0
- agentpack_cli-0.1.0.dist-info/entry_points.txt +2 -0
- agentpack_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
agentpack/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from agentpack.core.models import ContextPack
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseAdapter(ABC):
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def output_path(self, root: Path) -> Path:
|
|
12
|
+
...
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def render(self, pack: ContextPack) -> str:
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
def write(self, pack: ContextPack, root: Path) -> Path:
|
|
19
|
+
out = self.output_path(root)
|
|
20
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
out.write_text(self.render(pack))
|
|
22
|
+
return out
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agentpack.adapters.base import BaseAdapter
|
|
6
|
+
from agentpack.core.models import ContextPack
|
|
7
|
+
from agentpack.renderers.markdown import render_claude
|
|
8
|
+
|
|
9
|
+
# Re-export installer symbols for backward compatibility
|
|
10
|
+
from agentpack.installers.claude import ( # noqa: F401
|
|
11
|
+
ClaudeInstaller,
|
|
12
|
+
_AGENTPACK_BLOCK,
|
|
13
|
+
_BLOCK_RE,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ClaudeAdapter(BaseAdapter):
|
|
18
|
+
def __init__(self, output: str = ".agentpack/context.claude.md"):
|
|
19
|
+
self._output = output
|
|
20
|
+
|
|
21
|
+
def output_path(self, root: Path) -> Path:
|
|
22
|
+
return root / self._output
|
|
23
|
+
|
|
24
|
+
def render(self, pack: ContextPack) -> str:
|
|
25
|
+
return render_claude(pack)
|
|
26
|
+
|
|
27
|
+
# Delegating install methods — kept for backward compat with any callers using adapter directly
|
|
28
|
+
def patch_claude_md(self, root: Path) -> str:
|
|
29
|
+
return ClaudeInstaller().patch_claude_md(root)
|
|
30
|
+
|
|
31
|
+
def patch_claude_settings(self, root: Path, global_install: bool = False) -> str:
|
|
32
|
+
return ClaudeInstaller().patch_claude_settings(root, global_install)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agentpack.adapters.base import BaseAdapter
|
|
6
|
+
from agentpack.core.models import ContextPack
|
|
7
|
+
from agentpack.renderers.markdown import render_generic
|
|
8
|
+
from agentpack.installers.codex import CodexInstaller # noqa: F401
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CodexAdapter(BaseAdapter):
|
|
12
|
+
def __init__(self, output: str = ".agentpack/context.md"):
|
|
13
|
+
self._output = output
|
|
14
|
+
|
|
15
|
+
def output_path(self, root: Path) -> Path:
|
|
16
|
+
return root / self._output
|
|
17
|
+
|
|
18
|
+
def render(self, pack: ContextPack) -> str:
|
|
19
|
+
return render_generic(pack)
|
|
20
|
+
|
|
21
|
+
# Delegating install methods — kept for backward compat
|
|
22
|
+
def patch_agents_md(self, root: Path) -> str:
|
|
23
|
+
return CodexInstaller().patch_agents_md(root)
|
|
24
|
+
|
|
25
|
+
def install_auto_repack(self, root: Path) -> dict[str, str]:
|
|
26
|
+
return CodexInstaller().install_auto_repack(root)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agentpack.adapters.base import BaseAdapter
|
|
6
|
+
from agentpack.core.models import ContextPack
|
|
7
|
+
from agentpack.renderers.markdown import render_generic
|
|
8
|
+
from agentpack.installers.cursor import CursorInstaller # noqa: F401
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CursorAdapter(BaseAdapter):
|
|
12
|
+
def __init__(self, output: str = ".agentpack/context.md"):
|
|
13
|
+
self._output = output
|
|
14
|
+
|
|
15
|
+
def output_path(self, root: Path) -> Path:
|
|
16
|
+
return root / self._output
|
|
17
|
+
|
|
18
|
+
def render(self, pack: ContextPack) -> str:
|
|
19
|
+
return render_generic(pack)
|
|
20
|
+
|
|
21
|
+
# Delegating install methods — kept for backward compat
|
|
22
|
+
def patch_cursor_rules(self, root: Path) -> str:
|
|
23
|
+
return CursorInstaller().patch_cursor_rules(root)
|
|
24
|
+
|
|
25
|
+
def patch_cursor_mdc(self, root: Path) -> str:
|
|
26
|
+
return CursorInstaller().patch_cursor_mdc(root)
|
|
27
|
+
|
|
28
|
+
def install_auto_repack(self, root: Path) -> dict[str, str]:
|
|
29
|
+
return CursorInstaller().install_auto_repack(root)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agentpack.adapters.base import BaseAdapter
|
|
6
|
+
from agentpack.core.models import ContextPack
|
|
7
|
+
from agentpack.renderers.markdown import render_generic
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GenericAdapter(BaseAdapter):
|
|
11
|
+
def __init__(self, output: str = ".agentpack/context.md"):
|
|
12
|
+
self._output = output
|
|
13
|
+
|
|
14
|
+
def output_path(self, root: Path) -> Path:
|
|
15
|
+
return root / self._output
|
|
16
|
+
|
|
17
|
+
def render(self, pack: ContextPack) -> str:
|
|
18
|
+
return render_generic(pack)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agentpack.adapters.base import BaseAdapter
|
|
6
|
+
from agentpack.core.models import ContextPack
|
|
7
|
+
from agentpack.renderers.markdown import render_generic
|
|
8
|
+
from agentpack.installers.windsurf import WindsurfInstaller # noqa: F401
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WindsurfAdapter(BaseAdapter):
|
|
12
|
+
def __init__(self, output: str = ".agentpack/context.md"):
|
|
13
|
+
self._output = output
|
|
14
|
+
|
|
15
|
+
def output_path(self, root: Path) -> Path:
|
|
16
|
+
return root / self._output
|
|
17
|
+
|
|
18
|
+
def render(self, pack: ContextPack) -> str:
|
|
19
|
+
return render_generic(pack)
|
|
20
|
+
|
|
21
|
+
# Delegating install methods — kept for backward compat
|
|
22
|
+
def patch_windsurfrules(self, root: Path) -> str:
|
|
23
|
+
return WindsurfInstaller().patch_windsurfrules(root)
|
|
24
|
+
|
|
25
|
+
def install_auto_repack(self, root: Path) -> dict[str, str]:
|
|
26
|
+
return WindsurfInstaller().install_auto_repack(root)
|
|
File without changes
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agentpack.core.models import DependencyGraph, DependencyNode, FileInfo
|
|
6
|
+
from agentpack.analysis.python_imports import extract_imports as py_imports
|
|
7
|
+
from agentpack.analysis.python_imports import resolve_relative_import as py_resolve
|
|
8
|
+
from agentpack.analysis.js_ts_imports import extract_imports as js_imports
|
|
9
|
+
from agentpack.analysis.js_ts_imports import resolve_relative_import as js_resolve
|
|
10
|
+
from agentpack.analysis.go_imports import extract_imports as go_imports
|
|
11
|
+
from agentpack.analysis.rust_imports import extract_imports as rust_imports
|
|
12
|
+
from agentpack.analysis.java_imports import extract_imports as java_imports
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build(
|
|
16
|
+
files: list[FileInfo],
|
|
17
|
+
root: Path,
|
|
18
|
+
summaries: dict | None = None,
|
|
19
|
+
) -> DependencyGraph:
|
|
20
|
+
"""Build an import/imported-by graph over packable files.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
files: Packable (non-ignored, non-binary) FileInfo objects.
|
|
24
|
+
root: Repository root for resolving relative imports.
|
|
25
|
+
summaries: Optional pre-built summary cache; cached imports avoid re-parsing.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
DependencyGraph with typed DependencyNode entries. Caller fills tests
|
|
29
|
+
via find_related_tests after construction.
|
|
30
|
+
"""
|
|
31
|
+
graph = DependencyGraph(
|
|
32
|
+
nodes={fi.path: DependencyNode(path=fi.path) for fi in files}
|
|
33
|
+
)
|
|
34
|
+
path_set = {fi.path for fi in files}
|
|
35
|
+
|
|
36
|
+
for fi in files:
|
|
37
|
+
if summaries and fi.path in summaries:
|
|
38
|
+
cached_imports = summaries[fi.path].get("imports", [])
|
|
39
|
+
if cached_imports:
|
|
40
|
+
graph.nodes[fi.path].imports = cached_imports
|
|
41
|
+
for dep in cached_imports:
|
|
42
|
+
if dep in graph:
|
|
43
|
+
graph.nodes[dep].imported_by.append(fi.path)
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
raw_imports: list[str] = []
|
|
47
|
+
lang = fi.language
|
|
48
|
+
cached = fi.content
|
|
49
|
+
|
|
50
|
+
if lang == "python":
|
|
51
|
+
raw_imports = py_imports(fi.abs_path, cached)
|
|
52
|
+
elif lang in ("javascript", "typescript"):
|
|
53
|
+
raw_imports = js_imports(fi.abs_path, cached)
|
|
54
|
+
elif lang == "go":
|
|
55
|
+
raw_imports = go_imports(fi.abs_path, cached)
|
|
56
|
+
elif lang == "rust":
|
|
57
|
+
raw_imports = rust_imports(fi.abs_path, cached)
|
|
58
|
+
elif lang in ("java", "kotlin"):
|
|
59
|
+
raw_imports = java_imports(fi.abs_path, cached)
|
|
60
|
+
|
|
61
|
+
resolved: list[str] = []
|
|
62
|
+
for imp in raw_imports:
|
|
63
|
+
if imp.startswith("."):
|
|
64
|
+
if lang == "python":
|
|
65
|
+
r = py_resolve(fi.path, imp, root)
|
|
66
|
+
elif lang in ("javascript", "typescript"):
|
|
67
|
+
r = js_resolve(fi.path, imp, root)
|
|
68
|
+
else:
|
|
69
|
+
r = None
|
|
70
|
+
if r and r in path_set:
|
|
71
|
+
resolved.append(r)
|
|
72
|
+
else:
|
|
73
|
+
resolved.append(imp)
|
|
74
|
+
|
|
75
|
+
graph.nodes[fi.path].imports = resolved
|
|
76
|
+
for dep in resolved:
|
|
77
|
+
if dep in graph:
|
|
78
|
+
graph.nodes[dep].imported_by.append(fi.path)
|
|
79
|
+
|
|
80
|
+
return graph
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
_SINGLE = re.compile(r'^import\s+"([^"]+)"', re.MULTILINE)
|
|
7
|
+
_BLOCK_START = re.compile(r"^import\s+\(", re.MULTILINE)
|
|
8
|
+
_BLOCK_ENTRY = re.compile(r'^\s+(?:\w+\s+)?"([^"]+)"')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def extract_imports(path: Path, text: str | None = None) -> list[str]:
|
|
12
|
+
if text is None:
|
|
13
|
+
try:
|
|
14
|
+
text = path.read_text(errors="replace")
|
|
15
|
+
except OSError:
|
|
16
|
+
return []
|
|
17
|
+
|
|
18
|
+
imports: list[str] = []
|
|
19
|
+
imports.extend(m.group(1) for m in _SINGLE.finditer(text))
|
|
20
|
+
|
|
21
|
+
for block_m in _BLOCK_START.finditer(text):
|
|
22
|
+
rest = text[block_m.end():]
|
|
23
|
+
end = rest.find(")")
|
|
24
|
+
if end == -1:
|
|
25
|
+
continue
|
|
26
|
+
block = rest[:end]
|
|
27
|
+
for line in block.splitlines():
|
|
28
|
+
m = _BLOCK_ENTRY.match(line)
|
|
29
|
+
if m:
|
|
30
|
+
imports.append(m.group(1))
|
|
31
|
+
|
|
32
|
+
return imports
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
_IMPORT = re.compile(r"^import\s+(?:static\s+)?([\w.]+(?:\.\*)?)\s*;", re.MULTILINE)
|
|
7
|
+
_KOTLIN_IMPORT = re.compile(r"^import\s+([\w.]+(?:\.\*)?)", re.MULTILINE)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def extract_imports(path: Path, text: str | None = None) -> list[str]:
|
|
11
|
+
if text is None:
|
|
12
|
+
try:
|
|
13
|
+
text = path.read_text(errors="replace")
|
|
14
|
+
except OSError:
|
|
15
|
+
return []
|
|
16
|
+
|
|
17
|
+
suffix = path.suffix.lower()
|
|
18
|
+
pattern = _KOTLIN_IMPORT if suffix == ".kt" else _IMPORT
|
|
19
|
+
return [m.group(1) for m in pattern.finditer(text)]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
_IMPORT_PATTERNS = [
|
|
7
|
+
re.compile(r'import\s+.*?\s+from\s+["\']([^"\']+)["\']'),
|
|
8
|
+
re.compile(r'import\s+["\']([^"\']+)["\']'),
|
|
9
|
+
re.compile(r'require\s*\(\s*["\']([^"\']+)["\']\s*\)'),
|
|
10
|
+
re.compile(r'export\s+.*?\s+from\s+["\']([^"\']+)["\']'),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
_RELATIVE_EXTS = (".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def extract_imports(path: Path, text: str | None = None) -> list[str]:
|
|
17
|
+
if text is None:
|
|
18
|
+
try:
|
|
19
|
+
text = path.read_text(errors="replace")
|
|
20
|
+
except OSError:
|
|
21
|
+
return []
|
|
22
|
+
|
|
23
|
+
imports: list[str] = []
|
|
24
|
+
for pattern in _IMPORT_PATTERNS:
|
|
25
|
+
for m in pattern.finditer(text):
|
|
26
|
+
imports.append(m.group(1))
|
|
27
|
+
return imports
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def resolve_relative_import(importer: str, import_str: str, root: Path) -> str | None:
|
|
31
|
+
if not import_str.startswith("."):
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
base = (root / importer).parent
|
|
35
|
+
candidate = (base / import_str).resolve()
|
|
36
|
+
|
|
37
|
+
for ext in _RELATIVE_EXTS:
|
|
38
|
+
p = candidate.with_suffix(ext)
|
|
39
|
+
if p.exists():
|
|
40
|
+
try:
|
|
41
|
+
return str(p.relative_to(root))
|
|
42
|
+
except ValueError:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
for ext in _RELATIVE_EXTS:
|
|
46
|
+
p = candidate / f"index{ext}"
|
|
47
|
+
if p.exists():
|
|
48
|
+
try:
|
|
49
|
+
return str(p.relative_to(root))
|
|
50
|
+
except ValueError:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
return None
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def extract_imports(path: Path, text: str | None = None) -> list[str]:
|
|
8
|
+
try:
|
|
9
|
+
source = text if text is not None else path.read_text(errors="replace")
|
|
10
|
+
tree = ast.parse(source)
|
|
11
|
+
except SyntaxError:
|
|
12
|
+
return []
|
|
13
|
+
|
|
14
|
+
imports: list[str] = []
|
|
15
|
+
for node in ast.walk(tree):
|
|
16
|
+
if isinstance(node, ast.Import):
|
|
17
|
+
for alias in node.names:
|
|
18
|
+
imports.append(alias.name)
|
|
19
|
+
elif isinstance(node, ast.ImportFrom):
|
|
20
|
+
module = node.module or ""
|
|
21
|
+
level = node.level or 0
|
|
22
|
+
prefix = "." * level
|
|
23
|
+
imports.append(f"{prefix}{module}" if module else prefix)
|
|
24
|
+
return imports
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def resolve_relative_import(importer: str, import_str: str, root: Path) -> str | None:
|
|
28
|
+
"""Resolve a relative Python import to a file path relative to root."""
|
|
29
|
+
if not import_str.startswith("."):
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
dots = len(import_str) - len(import_str.lstrip("."))
|
|
33
|
+
module = import_str[dots:].replace(".", "/")
|
|
34
|
+
|
|
35
|
+
base = Path(importer).parent
|
|
36
|
+
for _ in range(dots - 1):
|
|
37
|
+
base = base.parent
|
|
38
|
+
|
|
39
|
+
candidate = base / module
|
|
40
|
+
for suffix in (".py", "/__init__.py"):
|
|
41
|
+
full = root / (str(candidate) + suffix)
|
|
42
|
+
if full.exists():
|
|
43
|
+
return str((candidate).with_suffix(".py") if suffix == ".py" else candidate / "__init__.py")
|
|
44
|
+
|
|
45
|
+
return None
|