python-skills 1.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.
- python_skills/__init__.py +10 -0
- python_skills/__main__.py +6 -0
- python_skills/adapters/__init__.py +48 -0
- python_skills/adapters/agent_skills.py +415 -0
- python_skills/adapters/aider_adapter.py +226 -0
- python_skills/adapters/base.py +153 -0
- python_skills/adapters/claude.py +474 -0
- python_skills/adapters/cline.py +332 -0
- python_skills/adapters/codex.py +24 -0
- python_skills/adapters/continue_adapter.py +198 -0
- python_skills/adapters/cursor.py +327 -0
- python_skills/adapters/gemini.py +26 -0
- python_skills/adapters/goose.py +26 -0
- python_skills/adapters/junie.py +25 -0
- python_skills/adapters/kiro.py +382 -0
- python_skills/adapters/opencode.py +27 -0
- python_skills/adapters/roo.py +25 -0
- python_skills/adapters/universal.py +203 -0
- python_skills/adapters/vscode.py +27 -0
- python_skills/adapters/windsurf.py +26 -0
- python_skills/adapters/zed.py +27 -0
- python_skills/cli.py +326 -0
- python_skills/config.py +160 -0
- python_skills/detector.py +152 -0
- python_skills/installer.py +163 -0
- python_skills/markers.py +115 -0
- python_skills/skills/__init__.py +14 -0
- python_skills/skills/loader.py +171 -0
- python_skills/skills/metadata.py +152 -0
- python_skills/skills/registry.py +101 -0
- python_skills/state.py +204 -0
- python_skills-1.0.0.dist-info/METADATA +99 -0
- python_skills-1.0.0.dist-info/RECORD +105 -0
- python_skills-1.0.0.dist-info/WHEEL +4 -0
- python_skills-1.0.0.dist-info/entry_points.txt +2 -0
- python_skills-1.0.0.dist-info/licenses/LICENSE +21 -0
- skills/advanced_python.md +239 -0
- skills/anti_patterns/index.md +406 -0
- skills/comprehensions.md +167 -0
- skills/control_flow.md +175 -0
- skills/data_structures.md +243 -0
- skills/debugging/common_bugs.md +222 -0
- skills/debugging/inspection_techniques.md +249 -0
- skills/debugging/root_cause.md +203 -0
- skills/engineering/application_logging.md +195 -0
- skills/engineering/cli_apps.md +207 -0
- skills/engineering/configuration.md +218 -0
- skills/engineering/database.md +240 -0
- skills/engineering/dependency_management.md +205 -0
- skills/engineering/http_clients.md +267 -0
- skills/engineering/modules_packages.md +211 -0
- skills/engineering/packaging.md +197 -0
- skills/engineering/project_structure.md +155 -0
- skills/engineering/pyproject_toml.md +302 -0
- skills/engineering/virtual_environments.md +206 -0
- skills/functions.md +244 -0
- skills/generation/async_concurrency.md +291 -0
- skills/generation/error_handling.md +276 -0
- skills/generation/protocols_generics.md +243 -0
- skills/generation/type_hints.md +290 -0
- skills/generation/validation_pipeline.md +274 -0
- skills/generation/workflow.md +190 -0
- skills/oop.md +228 -0
- skills/quality/abstractions.md +154 -0
- skills/quality/comments.md +177 -0
- skills/quality/documentation.md +176 -0
- skills/quality/duplication.md +137 -0
- skills/quality/maintainability.md +142 -0
- skills/quality/naming.md +171 -0
- skills/quality/quality_functions.md +245 -0
- skills/quality/readability.md +239 -0
- skills/quality/type_annotations.md +192 -0
- skills/refactoring/behavior_preservation.md +157 -0
- skills/refactoring/incremental.md +187 -0
- skills/refactoring/interface_stability.md +199 -0
- skills/refactoring/safe_refactoring.md +206 -0
- skills/security/auth_boundaries.md +200 -0
- skills/security/command_injection.md +207 -0
- skills/security/dependency_risks.md +282 -0
- skills/security/file_handling.md +156 -0
- skills/security/input_validation.md +190 -0
- skills/security/path_traversal.md +172 -0
- skills/security/secrets.md +171 -0
- skills/security/sql_injection.md +188 -0
- skills/security/unsafe_deserialization.md +164 -0
- skills/stdlib/argparse.md +178 -0
- skills/stdlib/collections.md +212 -0
- skills/stdlib/datetime.md +187 -0
- skills/stdlib/functools.md +238 -0
- skills/stdlib/itertools.md +183 -0
- skills/stdlib/json.md +162 -0
- skills/stdlib/logging.md +185 -0
- skills/stdlib/os_sys.md +184 -0
- skills/stdlib/pathlib.md +218 -0
- skills/stdlib/re.md +171 -0
- skills/stdlib/statistics.md +112 -0
- skills/stdlib/subprocess.md +211 -0
- skills/testing/async_tests.md +249 -0
- skills/testing/coverage.md +168 -0
- skills/testing/edge_cases.md +197 -0
- skills/testing/fixtures_mocks.md +203 -0
- skills/testing/organization.md +205 -0
- skills/testing/parameterized.md +174 -0
- skills/testing/regression_tests.md +165 -0
- skills/variables_types.md +107 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Aider adapter for python-skills.
|
|
2
|
+
|
|
3
|
+
Aider uses a "declare-everything" model — no auto-discovery of instruction files.
|
|
4
|
+
Integration is via `read:` key in `.aider.conf.yml` or `--read` CLI flag.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from ..adapters.base import (
|
|
10
|
+
AgentAdapter,
|
|
11
|
+
DetectionResult,
|
|
12
|
+
InstallResult,
|
|
13
|
+
StatusResult,
|
|
14
|
+
SyncResult,
|
|
15
|
+
UninstallResult,
|
|
16
|
+
)
|
|
17
|
+
from ..config import Target
|
|
18
|
+
from ..skills.registry import SkillRegistry
|
|
19
|
+
from ..state import LockManager
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AiderAdapter(AgentAdapter):
|
|
23
|
+
"""Adapter for Aider.
|
|
24
|
+
|
|
25
|
+
Aider does not auto-discover instruction files. We generate:
|
|
26
|
+
1. A PYTHON_SKILLS.md summary file
|
|
27
|
+
2. Update .aider.conf.yml to read it
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
name = "Aider"
|
|
31
|
+
target = Target.AIDER.value
|
|
32
|
+
|
|
33
|
+
def __init__(self, project_root: Path, skills_registry: SkillRegistry,
|
|
34
|
+
lock_manager: LockManager, config):
|
|
35
|
+
super().__init__(project_root, skills_registry, lock_manager, config)
|
|
36
|
+
|
|
37
|
+
def _get_global_path(self) -> Path:
|
|
38
|
+
return Path.home()
|
|
39
|
+
|
|
40
|
+
def detect(self) -> DetectionResult:
|
|
41
|
+
import shutil
|
|
42
|
+
app_detected = shutil.which("aider") is not None
|
|
43
|
+
project_config = (self.project_root / ".aider.conf.yml").exists()
|
|
44
|
+
global_config = (Path.home() / ".aider.conf.yml").exists()
|
|
45
|
+
|
|
46
|
+
return DetectionResult(
|
|
47
|
+
application_detected=app_detected,
|
|
48
|
+
project_config_detected=project_config,
|
|
49
|
+
adapter_available=True,
|
|
50
|
+
details=f"App: {'yes' if app_detected else 'no'}, "
|
|
51
|
+
f"Project: {'yes' if project_config else 'no'}, "
|
|
52
|
+
f"Global: {'yes' if global_config else 'no'}",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def install(self, scope: str = "project", dry_run: bool = False) -> InstallResult:
|
|
56
|
+
result = InstallResult(success=True, target=self.target, scope=scope)
|
|
57
|
+
target_path = self._get_scope_path(scope)
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
# Generate PYTHON_SKILLS.md
|
|
61
|
+
skills_md = target_path / "PYTHON_SKILLS.md"
|
|
62
|
+
content = self._generate_skills_summary()
|
|
63
|
+
|
|
64
|
+
if dry_run:
|
|
65
|
+
result.files_created.append(str(skills_md.relative_to(self.project_root)))
|
|
66
|
+
else:
|
|
67
|
+
self._safe_write_file(skills_md, content, dry_run)
|
|
68
|
+
result.files_created.append(str(skills_md.relative_to(self.project_root)))
|
|
69
|
+
self._record_file(self.target, scope, skills_md)
|
|
70
|
+
|
|
71
|
+
# Update .aider.conf.yml
|
|
72
|
+
config_path = target_path / ".aider.conf.yml"
|
|
73
|
+
config_created = self._update_aider_config(config_path, dry_run)
|
|
74
|
+
if config_created:
|
|
75
|
+
result.files_created.append(config_created)
|
|
76
|
+
|
|
77
|
+
if not dry_run:
|
|
78
|
+
self._update_lock_state(scope)
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
result.success = False
|
|
82
|
+
result.errors.append(str(e))
|
|
83
|
+
|
|
84
|
+
return result
|
|
85
|
+
|
|
86
|
+
def _generate_skills_summary(self) -> str:
|
|
87
|
+
categories = {}
|
|
88
|
+
if self.skills_registry:
|
|
89
|
+
for skill in self.skills_registry.get_all_skills():
|
|
90
|
+
if skill.category not in categories:
|
|
91
|
+
categories[skill.category] = []
|
|
92
|
+
categories[skill.category].append(skill.name)
|
|
93
|
+
|
|
94
|
+
parts = [
|
|
95
|
+
"<!-- BEGIN PYTHON-SKILLS MANAGED -->",
|
|
96
|
+
"# Python Skills Integration",
|
|
97
|
+
"",
|
|
98
|
+
"This project uses python-skills for Python engineering standards.",
|
|
99
|
+
"",
|
|
100
|
+
"## Available Skills",
|
|
101
|
+
"",
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
for category, skills in sorted(categories.items()):
|
|
105
|
+
parts.append(f"### {category.title()}")
|
|
106
|
+
parts.append("")
|
|
107
|
+
for skill in sorted(skills):
|
|
108
|
+
parts.append(f"- `{skill}`")
|
|
109
|
+
parts.append("")
|
|
110
|
+
|
|
111
|
+
parts.extend([
|
|
112
|
+
"## Key Patterns",
|
|
113
|
+
"",
|
|
114
|
+
"- **Type hints**: Use built-in generics, `str | None`, `Protocol`",
|
|
115
|
+
"- **Async**: `asyncio.gather`, `Semaphore`, `TaskGroup` (3.11+)",
|
|
116
|
+
"- **HTTP**: `httpx` with retries, timeouts, connection pooling",
|
|
117
|
+
"- **Security**: Parameterized queries, `pathlib` for paths, `yaml.safe_load`",
|
|
118
|
+
"- **Testing**: `pytest` with parametrized tests, edge cases",
|
|
119
|
+
"",
|
|
120
|
+
"<!-- END PYTHON-SKILLS MANAGED -->",
|
|
121
|
+
])
|
|
122
|
+
|
|
123
|
+
return "\n".join(parts)
|
|
124
|
+
|
|
125
|
+
def _update_aider_config(self, config_path: Path, dry_run: bool) -> str | None:
|
|
126
|
+
"""Update .aider.conf.yml to read PYTHON_SKILLS.md."""
|
|
127
|
+
import yaml
|
|
128
|
+
|
|
129
|
+
existing = {}
|
|
130
|
+
if config_path.exists():
|
|
131
|
+
try:
|
|
132
|
+
existing = yaml.safe_load(config_path.read_text(encoding="utf-8")) or {}
|
|
133
|
+
except Exception:
|
|
134
|
+
existing = {}
|
|
135
|
+
|
|
136
|
+
read_list = existing.get("read", [])
|
|
137
|
+
if not isinstance(read_list, list):
|
|
138
|
+
read_list = [read_list] if read_list else []
|
|
139
|
+
|
|
140
|
+
if "PYTHON_SKILLS.md" not in read_list:
|
|
141
|
+
read_list.append("PYTHON_SKILLS.md")
|
|
142
|
+
existing["read"] = read_list
|
|
143
|
+
|
|
144
|
+
if not dry_run:
|
|
145
|
+
content = yaml.dump(existing, default_flow_style=False, sort_keys=True)
|
|
146
|
+
self._safe_write_file(config_path, content, dry_run)
|
|
147
|
+
|
|
148
|
+
return str(config_path.relative_to(self.project_root))
|
|
149
|
+
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
def sync(self, scope: str = "project", dry_run: bool = False) -> SyncResult:
|
|
153
|
+
result = SyncResult(success=True, target=self.target, scope=scope)
|
|
154
|
+
target_path = self._get_scope_path(scope)
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
skills_md = target_path / "PYTHON_SKILLS.md"
|
|
158
|
+
if not skills_md.exists():
|
|
159
|
+
result.success = False
|
|
160
|
+
result.errors.append("PYTHON_SKILLS.md not found")
|
|
161
|
+
return result
|
|
162
|
+
|
|
163
|
+
current = skills_md.read_text(encoding="utf-8")
|
|
164
|
+
new_content = self._generate_skills_summary()
|
|
165
|
+
|
|
166
|
+
if current.strip() != new_content.strip():
|
|
167
|
+
if not dry_run:
|
|
168
|
+
self._safe_write_file(skills_md, new_content, dry_run)
|
|
169
|
+
result.modified.append(str(skills_md.relative_to(self.project_root)))
|
|
170
|
+
else:
|
|
171
|
+
result.skipped.append(str(skills_md.relative_to(self.project_root)))
|
|
172
|
+
|
|
173
|
+
if not dry_run:
|
|
174
|
+
self._update_lock_state(scope)
|
|
175
|
+
|
|
176
|
+
except Exception as e:
|
|
177
|
+
result.success = False
|
|
178
|
+
result.errors.append(str(e))
|
|
179
|
+
|
|
180
|
+
return result
|
|
181
|
+
|
|
182
|
+
def uninstall(self, scope: str = "project", dry_run: bool = False) -> UninstallResult:
|
|
183
|
+
result = UninstallResult(success=True, target=self.target, scope=scope)
|
|
184
|
+
target_path = self._get_scope_path(scope)
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
skills_md = target_path / "PYTHON_SKILLS.md"
|
|
188
|
+
if skills_md.exists():
|
|
189
|
+
content = skills_md.read_text(encoding="utf-8")
|
|
190
|
+
begin, end = self._get_markers(skills_md)
|
|
191
|
+
if begin in content:
|
|
192
|
+
if not dry_run:
|
|
193
|
+
skills_md.unlink()
|
|
194
|
+
result.files_removed.append(str(skills_md.relative_to(self.project_root)))
|
|
195
|
+
|
|
196
|
+
if not dry_run:
|
|
197
|
+
self._update_lock_state(scope)
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
result.success = False
|
|
201
|
+
result.errors.append(str(e))
|
|
202
|
+
|
|
203
|
+
return result
|
|
204
|
+
|
|
205
|
+
def status(self, scope: str = "project") -> StatusResult:
|
|
206
|
+
target_path = self._get_scope_path(scope)
|
|
207
|
+
skills_md = target_path / "PYTHON_SKILLS.md"
|
|
208
|
+
|
|
209
|
+
installed = False
|
|
210
|
+
files = []
|
|
211
|
+
if skills_md.exists():
|
|
212
|
+
content = skills_md.read_text(encoding="utf-8")
|
|
213
|
+
begin, end = self._get_markers(skills_md)
|
|
214
|
+
if begin in content:
|
|
215
|
+
installed = True
|
|
216
|
+
files.append(str(skills_md.relative_to(self.project_root)))
|
|
217
|
+
|
|
218
|
+
return StatusResult(
|
|
219
|
+
target=self.target, scope=scope, installed=installed,
|
|
220
|
+
files=files, version="2.0.0",
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
def _update_lock_state(self, scope: str) -> None:
|
|
224
|
+
skills_root = self.skills_registry.skills_root
|
|
225
|
+
if skills_root.exists():
|
|
226
|
+
self.lock_manager.update_canonical_hash(skills_root)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Base adapter contract for python-skills adapters."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class DetectionResult:
|
|
10
|
+
"""Result of environment detection."""
|
|
11
|
+
application_detected: bool
|
|
12
|
+
project_config_detected: bool
|
|
13
|
+
adapter_available: bool = True
|
|
14
|
+
details: str = ""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class InstallResult:
|
|
19
|
+
"""Result of installation."""
|
|
20
|
+
success: bool
|
|
21
|
+
target: str
|
|
22
|
+
scope: str
|
|
23
|
+
files_created: list[str] = field(default_factory=list)
|
|
24
|
+
files_modified: list[str] = field(default_factory=list)
|
|
25
|
+
errors: list[str] = field(default_factory=list)
|
|
26
|
+
warnings: list[str] = field(default_factory=list)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class SyncResult:
|
|
31
|
+
"""Result of synchronization."""
|
|
32
|
+
success: bool
|
|
33
|
+
target: str
|
|
34
|
+
scope: str
|
|
35
|
+
added: list[str] = field(default_factory=list)
|
|
36
|
+
modified: list[str] = field(default_factory=list)
|
|
37
|
+
removed: list[str] = field(default_factory=list)
|
|
38
|
+
skipped: list[str] = field(default_factory=list)
|
|
39
|
+
errors: list[str] = field(default_factory=list)
|
|
40
|
+
warnings: list[str] = field(default_factory=list)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class UninstallResult:
|
|
45
|
+
"""Result of uninstallation."""
|
|
46
|
+
success: bool
|
|
47
|
+
target: str
|
|
48
|
+
scope: str
|
|
49
|
+
files_removed: list[str] = field(default_factory=list)
|
|
50
|
+
regions_removed: list[str] = field(default_factory=list)
|
|
51
|
+
skipped: list[str] = field(default_factory=list)
|
|
52
|
+
errors: list[str] = field(default_factory=list)
|
|
53
|
+
warnings: list[str] = field(default_factory=list)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class StatusResult:
|
|
58
|
+
"""Result of status check."""
|
|
59
|
+
target: str
|
|
60
|
+
scope: str
|
|
61
|
+
installed: bool
|
|
62
|
+
files: list[str] = field(default_factory=list)
|
|
63
|
+
version: str = ""
|
|
64
|
+
last_sync: str = ""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class AgentAdapter(ABC):
|
|
68
|
+
"""Abstract base class for agent adapters."""
|
|
69
|
+
|
|
70
|
+
name: str = ""
|
|
71
|
+
target: str = ""
|
|
72
|
+
|
|
73
|
+
def __init__(self, project_root: Path, skills_registry, lock_manager, config):
|
|
74
|
+
self.project_root = Path(project_root).resolve()
|
|
75
|
+
self.skills_registry = skills_registry
|
|
76
|
+
self.lock_manager = lock_manager
|
|
77
|
+
self.config = config
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def detect(self) -> DetectionResult:
|
|
81
|
+
"""Detect if this agent is available and configured."""
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
@abstractmethod
|
|
85
|
+
def install(self, scope: str = "project", dry_run: bool = False) -> InstallResult:
|
|
86
|
+
"""Install integration for this agent."""
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def sync(self, scope: str = "project", dry_run: bool = False) -> SyncResult:
|
|
91
|
+
"""Synchronize installed integration with current skills."""
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
@abstractmethod
|
|
95
|
+
def uninstall(self, scope: str = "project", dry_run: bool = False) -> UninstallResult:
|
|
96
|
+
"""Uninstall integration for this agent."""
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
@abstractmethod
|
|
100
|
+
def status(self, scope: str = "project") -> StatusResult:
|
|
101
|
+
"""Get installation status."""
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
def _get_scope_path(self, scope: str) -> Path:
|
|
105
|
+
"""Get the path for a given scope."""
|
|
106
|
+
if scope == "global":
|
|
107
|
+
return self._get_global_path()
|
|
108
|
+
return self.project_root
|
|
109
|
+
|
|
110
|
+
@abstractmethod
|
|
111
|
+
def _get_global_path(self) -> Path:
|
|
112
|
+
"""Get the global config path for this agent."""
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
def _get_markers(self, file_path: Path) -> tuple[str, str]:
|
|
116
|
+
"""Get ownership markers for a file."""
|
|
117
|
+
from ..markers import get_markers
|
|
118
|
+
return get_markers(str(file_path))
|
|
119
|
+
|
|
120
|
+
def _wrap_managed(self, content: str, file_path: Path) -> str:
|
|
121
|
+
"""Wrap content with ownership markers."""
|
|
122
|
+
from ..markers import wrap_managed_content
|
|
123
|
+
return wrap_managed_content(content, str(file_path))
|
|
124
|
+
|
|
125
|
+
def _replace_managed_region(self, content: str, new_content: str, file_path: Path) -> tuple[str, bool]:
|
|
126
|
+
"""Replace managed region in content."""
|
|
127
|
+
from ..markers import replace_managed_region
|
|
128
|
+
return replace_managed_region(content, new_content, str(file_path))
|
|
129
|
+
|
|
130
|
+
def _remove_managed_region(self, content: str, file_path: Path) -> tuple[str, bool]:
|
|
131
|
+
"""Remove managed region from content."""
|
|
132
|
+
from ..markers import remove_managed_region
|
|
133
|
+
return remove_managed_region(content, str(file_path))
|
|
134
|
+
|
|
135
|
+
def _extract_managed_region(self, content: str, file_path: Path) -> str | None:
|
|
136
|
+
"""Extract managed region from content."""
|
|
137
|
+
from ..markers import extract_managed_region
|
|
138
|
+
return extract_managed_region(content, str(file_path))
|
|
139
|
+
|
|
140
|
+
def _safe_write_file(self, path: Path, content: str, dry_run: bool = False) -> bool:
|
|
141
|
+
"""Safely write a file with ownership markers."""
|
|
142
|
+
if dry_run:
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
146
|
+
path.write_text(content, encoding="utf-8")
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
def _record_file(self, target: str, scope: str, path: Path, region_markers: tuple = None) -> None:
|
|
150
|
+
"""Record a managed file in the lock state."""
|
|
151
|
+
self.lock_manager.record_file(target, scope, path, region_markers)
|
|
152
|
+
|
|
153
|
+
|