deproc-core 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.
- deproc/core/__init__.py +5 -0
- deproc/core/context.py +207 -0
- deproc/core/discovery.py +23 -0
- deproc/core/interfaces/__init__.py +11 -0
- deproc/core/interfaces/linker.py +15 -0
- deproc/core/interfaces/parser/__init__.py +5 -0
- deproc/core/interfaces/parser/base.py +11 -0
- deproc/core/interfaces/parser/models.py +142 -0
- deproc/core/interfaces/resolver.py +16 -0
- deproc/core/interfaces/symbol_cache.py +20 -0
- deproc/core/runtime/__init__.py +7 -0
- deproc/core/runtime/registries/__init__.py +5 -0
- deproc/core/runtime/registries/entity.py +42 -0
- deproc_core-0.1.0.dist-info/METADATA +19 -0
- deproc_core-0.1.0.dist-info/RECORD +17 -0
- deproc_core-0.1.0.dist-info/WHEEL +5 -0
- deproc_core-0.1.0.dist-info/top_level.txt +1 -0
deproc/core/__init__.py
ADDED
deproc/core/context.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from .interfaces.linker import Linker
|
|
8
|
+
from .interfaces.parser import SourceParser
|
|
9
|
+
from .interfaces.resolver import Resolver
|
|
10
|
+
from .interfaces.symbol_cache import SymbolCache
|
|
11
|
+
|
|
12
|
+
from .runtime import EntityRegistry
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
class Context:
|
|
17
|
+
def __init__(self, base_path: str = "", copy_from: Context | None = None):
|
|
18
|
+
if copy_from:
|
|
19
|
+
self.base_path = copy_from.base_path
|
|
20
|
+
self.entity_registry = copy_from.entity_registry
|
|
21
|
+
self._language_aliases = dict(copy_from._language_aliases)
|
|
22
|
+
self._language_extensions = dict(copy_from._language_extensions)
|
|
23
|
+
self._all_languages = set(copy_from._all_languages)
|
|
24
|
+
self._all_file_extensions = set(copy_from._all_file_extensions)
|
|
25
|
+
self._selected_languages = set(copy_from._selected_languages)
|
|
26
|
+
self._selected_file_extensions = set(copy_from._selected_file_extensions)
|
|
27
|
+
self._parsers = dict(copy_from._parsers)
|
|
28
|
+
self._resolvers = dict(copy_from._resolvers)
|
|
29
|
+
self._linkers = dict(copy_from._linkers)
|
|
30
|
+
self.symbol_caches = dict(copy_from.symbol_caches)
|
|
31
|
+
self._skip_paths = set(copy_from._skip_paths)
|
|
32
|
+
else:
|
|
33
|
+
self.base_path = base_path
|
|
34
|
+
self.entity_registry = EntityRegistry()
|
|
35
|
+
self._language_aliases: dict[str, list[str]] = {}
|
|
36
|
+
self._language_extensions: dict[str, list[str]] = {}
|
|
37
|
+
self._all_languages: set[str] = set()
|
|
38
|
+
self._all_file_extensions: set[str] = set()
|
|
39
|
+
self._selected_languages: set[str] = set()
|
|
40
|
+
self._selected_file_extensions: set[str] = set()
|
|
41
|
+
self._skip_paths: set[str] = set()
|
|
42
|
+
self._parsers: dict[str, SourceParser] = {}
|
|
43
|
+
self._resolvers: dict[str, Resolver] = {}
|
|
44
|
+
self._linkers: dict[str, Linker] = {}
|
|
45
|
+
self.symbol_caches: dict[str, SymbolCache] = {}
|
|
46
|
+
|
|
47
|
+
def set_language(
|
|
48
|
+
self,
|
|
49
|
+
language: str,
|
|
50
|
+
file_extensions: list[str],
|
|
51
|
+
aliases: list[str] | None = None,
|
|
52
|
+
) -> None:
|
|
53
|
+
normalized = language.strip().lower()
|
|
54
|
+
normalized_extensions: list[str] = []
|
|
55
|
+
for ext in file_extensions:
|
|
56
|
+
e = ext.strip().lower()
|
|
57
|
+
if not e.startswith("."):
|
|
58
|
+
e = f".{e}"
|
|
59
|
+
logger.warning("Normalized file extension '%s' to '%s' for language '%s'", ext, e, normalized)
|
|
60
|
+
normalized_extensions.append(e)
|
|
61
|
+
self._language_extensions[normalized] = normalized_extensions
|
|
62
|
+
if aliases:
|
|
63
|
+
self._language_aliases[normalized] = [a.strip().lower() for a in aliases]
|
|
64
|
+
else:
|
|
65
|
+
self._language_aliases[normalized] = []
|
|
66
|
+
self._all_languages.add(normalized)
|
|
67
|
+
self._selected_languages.add(normalized)
|
|
68
|
+
self._all_file_extensions.update(normalized_extensions)
|
|
69
|
+
self._selected_file_extensions.update(normalized_extensions)
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def selected_languages(self) -> set[str]:
|
|
73
|
+
return self._selected_languages
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def selected_file_extensions(self) -> set[str]:
|
|
77
|
+
return self._selected_file_extensions
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def skip_paths(self) -> set[str]:
|
|
81
|
+
return self._skip_paths
|
|
82
|
+
|
|
83
|
+
def set_skip_paths(self, paths: set[str]) -> None:
|
|
84
|
+
self._skip_paths = set(paths)
|
|
85
|
+
|
|
86
|
+
def _normalize_language(self, language: str) -> str:
|
|
87
|
+
normalized = language.strip().lower()
|
|
88
|
+
for canonical, aliases in self._language_aliases.items():
|
|
89
|
+
if normalized == canonical or normalized in aliases:
|
|
90
|
+
return canonical
|
|
91
|
+
return normalized
|
|
92
|
+
|
|
93
|
+
def _normalize_extension(self, extension: str) -> str:
|
|
94
|
+
normalized = extension.strip().lower()
|
|
95
|
+
if not normalized.startswith("."):
|
|
96
|
+
normalized = f".{normalized}"
|
|
97
|
+
return normalized
|
|
98
|
+
|
|
99
|
+
def select_languages(self, languages: list[str]) -> None:
|
|
100
|
+
for lang in languages:
|
|
101
|
+
normalized = self._normalize_language(lang)
|
|
102
|
+
exclude = normalized.startswith("!")
|
|
103
|
+
if exclude:
|
|
104
|
+
normalized = normalized[1:]
|
|
105
|
+
if normalized in self._all_languages:
|
|
106
|
+
if exclude:
|
|
107
|
+
self._selected_languages.discard(normalized)
|
|
108
|
+
else:
|
|
109
|
+
self._selected_languages.add(normalized)
|
|
110
|
+
|
|
111
|
+
def select_file_extensions(self, extensions: list[str]) -> None:
|
|
112
|
+
for ext in extensions:
|
|
113
|
+
raw = ext.strip().lower()
|
|
114
|
+
exclude = raw.startswith("!")
|
|
115
|
+
if exclude:
|
|
116
|
+
raw = raw[1:]
|
|
117
|
+
normalized = self._normalize_extension(raw)
|
|
118
|
+
if normalized in self._all_file_extensions:
|
|
119
|
+
if exclude:
|
|
120
|
+
self._selected_file_extensions.discard(normalized)
|
|
121
|
+
else:
|
|
122
|
+
self._selected_file_extensions.add(normalized)
|
|
123
|
+
|
|
124
|
+
def _require_language(self, language: str) -> None:
|
|
125
|
+
if language not in self._all_languages:
|
|
126
|
+
raise KeyError(f"Language '{language}' is not registered. Call set_language() first.")
|
|
127
|
+
|
|
128
|
+
def _warn_if_language_missing(self, language: str) -> None:
|
|
129
|
+
if language not in self._all_languages:
|
|
130
|
+
logger.warning("Language '%s' is not registered. Did you forget to call set_language()?", language)
|
|
131
|
+
|
|
132
|
+
def set_parser(self, language: str, parser: SourceParser) -> None:
|
|
133
|
+
self._require_language(language)
|
|
134
|
+
self._parsers[language] = parser
|
|
135
|
+
|
|
136
|
+
def get_parser(self, language: str) -> SourceParser | None:
|
|
137
|
+
self._warn_if_language_missing(language)
|
|
138
|
+
return self._parsers.get(language)
|
|
139
|
+
|
|
140
|
+
def has_parser(self, language: str) -> bool:
|
|
141
|
+
self._warn_if_language_missing(language)
|
|
142
|
+
return language in self._parsers
|
|
143
|
+
|
|
144
|
+
def remove_parser(self, language: str) -> None:
|
|
145
|
+
self._warn_if_language_missing(language)
|
|
146
|
+
self._parsers.pop(language, None)
|
|
147
|
+
|
|
148
|
+
def set_resolver(self, language: str, resolver: Resolver) -> None:
|
|
149
|
+
self._require_language(language)
|
|
150
|
+
self._resolvers[language] = resolver
|
|
151
|
+
|
|
152
|
+
def get_resolver(self, language: str) -> Resolver | None:
|
|
153
|
+
self._warn_if_language_missing(language)
|
|
154
|
+
return self._resolvers.get(language)
|
|
155
|
+
|
|
156
|
+
def has_resolver(self, language: str) -> bool:
|
|
157
|
+
self._warn_if_language_missing(language)
|
|
158
|
+
return language in self._resolvers
|
|
159
|
+
|
|
160
|
+
def remove_resolver(self, language: str) -> None:
|
|
161
|
+
self._warn_if_language_missing(language)
|
|
162
|
+
self._resolvers.pop(language, None)
|
|
163
|
+
|
|
164
|
+
def set_linker(self, language: str, linker: Linker) -> None:
|
|
165
|
+
self._require_language(language)
|
|
166
|
+
self._linkers[language] = linker
|
|
167
|
+
|
|
168
|
+
def get_linker(self, language: str) -> Linker | None:
|
|
169
|
+
self._warn_if_language_missing(language)
|
|
170
|
+
return self._linkers.get(language)
|
|
171
|
+
|
|
172
|
+
def has_linker(self, language: str) -> bool:
|
|
173
|
+
self._warn_if_language_missing(language)
|
|
174
|
+
return language in self._linkers
|
|
175
|
+
|
|
176
|
+
def remove_linker(self, language: str) -> None:
|
|
177
|
+
self._warn_if_language_missing(language)
|
|
178
|
+
self._linkers.pop(language, None)
|
|
179
|
+
|
|
180
|
+
def set_symbol_cache(self, cache: SymbolCache) -> None:
|
|
181
|
+
self._require_language(cache.language)
|
|
182
|
+
self.symbol_caches[cache.language] = cache
|
|
183
|
+
|
|
184
|
+
def get_symbol_cache(self, language: str) -> SymbolCache | None:
|
|
185
|
+
self._warn_if_language_missing(language)
|
|
186
|
+
return self.symbol_caches.get(language)
|
|
187
|
+
|
|
188
|
+
def has_symbol_cache(self, language: str) -> bool:
|
|
189
|
+
self._warn_if_language_missing(language)
|
|
190
|
+
return language in self.symbol_caches
|
|
191
|
+
|
|
192
|
+
def remove_symbol_cache(self, language: str) -> None:
|
|
193
|
+
self._warn_if_language_missing(language)
|
|
194
|
+
self.symbol_caches.pop(language, None)
|
|
195
|
+
|
|
196
|
+
def reset(
|
|
197
|
+
self,
|
|
198
|
+
include_languages: bool = True,
|
|
199
|
+
include_file_extensions: bool = True,
|
|
200
|
+
include_symbol_caches: bool = True,
|
|
201
|
+
) -> None:
|
|
202
|
+
if include_languages:
|
|
203
|
+
self._selected_languages = set(self._all_languages)
|
|
204
|
+
if include_file_extensions:
|
|
205
|
+
self._selected_file_extensions = set(self._all_file_extensions)
|
|
206
|
+
if include_symbol_caches:
|
|
207
|
+
self.symbol_caches = {}
|
deproc/core/discovery.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import fnmatch
|
|
2
|
+
import os
|
|
3
|
+
from .context import Context
|
|
4
|
+
|
|
5
|
+
def _match_any(name: str, patterns: set[str]) -> bool:
|
|
6
|
+
return any(fnmatch.fnmatch(name, p) for p in patterns)
|
|
7
|
+
|
|
8
|
+
def find_source_files(context: Context) -> list[str]:
|
|
9
|
+
root = context.base_path
|
|
10
|
+
extension_set = set(context.selected_file_extensions)
|
|
11
|
+
skip_dirs = context.skip_paths
|
|
12
|
+
if not extension_set:
|
|
13
|
+
return []
|
|
14
|
+
|
|
15
|
+
matches: list[str] = []
|
|
16
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
17
|
+
dirnames[:] = [d for d in dirnames if not _match_any(d, skip_dirs)]
|
|
18
|
+
for f in filenames:
|
|
19
|
+
if any(f.lower().endswith(ext) for ext in extension_set):
|
|
20
|
+
matches.append(os.path.join(dirpath, f))
|
|
21
|
+
|
|
22
|
+
matches.sort()
|
|
23
|
+
return matches
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING, Protocol, TypeVar, runtime_checkable
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from ..context import Context
|
|
6
|
+
|
|
7
|
+
from .parser.models import SourceFile, Node
|
|
8
|
+
|
|
9
|
+
T_In = TypeVar("T_In", bound=list[SourceFile])
|
|
10
|
+
T_Out = TypeVar("T_Out", bound=list[Node])
|
|
11
|
+
|
|
12
|
+
@runtime_checkable
|
|
13
|
+
class Linker(Protocol[T_In, T_Out]):
|
|
14
|
+
def link_files(self, nodes: T_In, context: Context) -> T_Out:
|
|
15
|
+
...
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from .models import SourceFile
|
|
6
|
+
from ...context import Context
|
|
7
|
+
|
|
8
|
+
@runtime_checkable
|
|
9
|
+
class SourceParser(Protocol):
|
|
10
|
+
def parse_file(self, file_path: str, context: Context) -> SourceFile:
|
|
11
|
+
...
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provisional data models for the parser interface.
|
|
3
|
+
Please use these models either directly in the plugin implementations or as a reference for defining plugin-specific models.
|
|
4
|
+
"""
|
|
5
|
+
import hashlib
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from uuid import NAMESPACE_URL, UUID, uuid4, uuid5
|
|
8
|
+
|
|
9
|
+
type SymbolID = str
|
|
10
|
+
|
|
11
|
+
def generate_id() -> SymbolID:
|
|
12
|
+
return uuid4().hex
|
|
13
|
+
|
|
14
|
+
@dataclass(kw_only=True)
|
|
15
|
+
class Entity:
|
|
16
|
+
id: str | None = None
|
|
17
|
+
parent_id: str | None = None
|
|
18
|
+
|
|
19
|
+
def __post_init__(self):
|
|
20
|
+
if self.id is not None:
|
|
21
|
+
return
|
|
22
|
+
self.id = self._compute_id()
|
|
23
|
+
|
|
24
|
+
def _compute_id(self) -> str:
|
|
25
|
+
parent_id = getattr(self, "parent_id", None)
|
|
26
|
+
source_range = getattr(self, "source_range", None)
|
|
27
|
+
if parent_id and source_range:
|
|
28
|
+
namespace = UUID(hex=parent_id)
|
|
29
|
+
name = f"{type(self).__qualname__}:{source_range.lineno}:{source_range.end_lineno}:{source_range.col_offset}:{source_range.end_col_offset}"
|
|
30
|
+
return uuid5(namespace, name).hex
|
|
31
|
+
return uuid4().hex
|
|
32
|
+
|
|
33
|
+
@dataclass(kw_only=True)
|
|
34
|
+
class SourceRange:
|
|
35
|
+
lineno: int
|
|
36
|
+
end_lineno: int
|
|
37
|
+
col_offset: int
|
|
38
|
+
end_col_offset: int
|
|
39
|
+
source_id: str | None = None
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class Docstring:
|
|
43
|
+
docstring_range: SourceRange | None
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class Signature:
|
|
47
|
+
signature_range: SourceRange
|
|
48
|
+
arguments_range: SourceRange | None
|
|
49
|
+
return_type_range: SourceRange | None
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class Annotation:
|
|
53
|
+
source_range: SourceRange
|
|
54
|
+
name: str
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class ImportStatement(Entity):
|
|
58
|
+
source_range: SourceRange
|
|
59
|
+
type: str
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class Argument:
|
|
63
|
+
name: str
|
|
64
|
+
source_range: SourceRange
|
|
65
|
+
default_value: str | None
|
|
66
|
+
type_annotation: str | None
|
|
67
|
+
|
|
68
|
+
@dataclass(kw_only=True)
|
|
69
|
+
class FunctionLike(Docstring, Entity):
|
|
70
|
+
name: str
|
|
71
|
+
fqn: str
|
|
72
|
+
source_range: SourceRange
|
|
73
|
+
type: str = field(default="FUNCTION")
|
|
74
|
+
signature: Signature
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class SimpleBinding:
|
|
78
|
+
name: str
|
|
79
|
+
fqn: str
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class ComplexBinding:
|
|
83
|
+
source_range: SourceRange | None
|
|
84
|
+
|
|
85
|
+
@dataclass(kw_only=True)
|
|
86
|
+
class VariableDeclaration(Entity):
|
|
87
|
+
type: str = field(default="VARIABLE")
|
|
88
|
+
source_range: SourceRange
|
|
89
|
+
variable_binding: SimpleBinding | ComplexBinding
|
|
90
|
+
value_range: SourceRange | None
|
|
91
|
+
type_annotation: SourceRange | None
|
|
92
|
+
modifiers: list[str] = field(default_factory=list)
|
|
93
|
+
|
|
94
|
+
@dataclass(kw_only=True)
|
|
95
|
+
class TypeDefinition(Docstring, Entity):
|
|
96
|
+
name: str
|
|
97
|
+
fqn: str
|
|
98
|
+
source_range: SourceRange
|
|
99
|
+
type: str = field(default="TYPE_DEFINITION")
|
|
100
|
+
annotations: list[Annotation] = field(default_factory=list)
|
|
101
|
+
inherits: list[str] = field(default_factory=list)
|
|
102
|
+
method_ids: list[str] = field(default_factory=list)
|
|
103
|
+
inner_type_ids: list[str] = field(default_factory=list)
|
|
104
|
+
property_ids: list[str] = field(default_factory=list)
|
|
105
|
+
visibility: str | None
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class ControlFlowBlock(Entity):
|
|
109
|
+
branch: str
|
|
110
|
+
source_range: SourceRange
|
|
111
|
+
condition_range: SourceRange | None
|
|
112
|
+
import_stmt_ids: list[SymbolID] = field(default_factory=list)
|
|
113
|
+
type_ids: list[SymbolID] = field(default_factory=list)
|
|
114
|
+
function_ids: list[SymbolID] = field(default_factory=list)
|
|
115
|
+
variable_ids: list[SymbolID] = field(default_factory=list)
|
|
116
|
+
nested_group_ids: list[SymbolID] = field(default_factory=list)
|
|
117
|
+
|
|
118
|
+
@dataclass
|
|
119
|
+
class ControlFlowGroup(Entity):
|
|
120
|
+
group_type: str
|
|
121
|
+
source_range: SourceRange
|
|
122
|
+
block_ids: list[SymbolID] = field(default_factory=list)
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
class Node(Entity):
|
|
126
|
+
path: str
|
|
127
|
+
|
|
128
|
+
def _compute_id(self) -> str:
|
|
129
|
+
return uuid5(NAMESPACE_URL, f"file://{self.path}").hex
|
|
130
|
+
|
|
131
|
+
@dataclass(kw_only=True)
|
|
132
|
+
class SourceFile(Docstring, Node):
|
|
133
|
+
import_stmt_ids: list[SymbolID] = field(default_factory=list)
|
|
134
|
+
type_ids: list[SymbolID] = field(default_factory=list)
|
|
135
|
+
function_ids: list[SymbolID] = field(default_factory=list)
|
|
136
|
+
variable_ids: list[SymbolID] = field(default_factory=list)
|
|
137
|
+
control_flow_group_ids: list[SymbolID] = field(default_factory=list)
|
|
138
|
+
source: str
|
|
139
|
+
|
|
140
|
+
def _compute_id(self) -> str:
|
|
141
|
+
h = hashlib.sha256(self.source.encode()).hexdigest()[:16]
|
|
142
|
+
return uuid5(NAMESPACE_URL, f"file://{self.path}#{h}").hex
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import (
|
|
3
|
+
Any,
|
|
4
|
+
Protocol,
|
|
5
|
+
TypeVar,
|
|
6
|
+
runtime_checkable
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
TOut = TypeVar("TOut")
|
|
10
|
+
|
|
11
|
+
@runtime_checkable
|
|
12
|
+
class Resolver(Protocol[TOut]):
|
|
13
|
+
def resolve(self, *args: Any, **kwargs: Any) -> TOut:
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
__all__ = ["Resolver"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from typing import(
|
|
2
|
+
Any,
|
|
3
|
+
Protocol,
|
|
4
|
+
TypeVar,
|
|
5
|
+
runtime_checkable
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
TCache = TypeVar("TCache")
|
|
9
|
+
TReturn = TypeVar("TReturn")
|
|
10
|
+
|
|
11
|
+
@runtime_checkable
|
|
12
|
+
class SymbolCache(Protocol[TCache, TReturn]):
|
|
13
|
+
language: str
|
|
14
|
+
cache: TCache
|
|
15
|
+
|
|
16
|
+
def get(self, *args: Any, **kwargs: Any) -> TReturn:
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
def set(self, *args: Any, **kwargs: Any) -> None:
|
|
20
|
+
...
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from ...interfaces.parser.models import (
|
|
4
|
+
Entity,
|
|
5
|
+
SymbolID,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class EntityRegistry:
|
|
10
|
+
entities: dict[SymbolID, Entity] = field(default_factory=dict)
|
|
11
|
+
fqn_to_ids: dict[str, set[SymbolID]] = field(default_factory=lambda: defaultdict(set))
|
|
12
|
+
|
|
13
|
+
def add(self, entity: Entity) -> None:
|
|
14
|
+
self.entities[entity.id] = entity
|
|
15
|
+
fqn = getattr(entity, "fqn", None)
|
|
16
|
+
if fqn:
|
|
17
|
+
self.fqn_to_ids[fqn].add(entity.id)
|
|
18
|
+
|
|
19
|
+
def add_all(self, entities: list[Entity]) -> None:
|
|
20
|
+
for entity in entities:
|
|
21
|
+
self.add(entity)
|
|
22
|
+
|
|
23
|
+
def remove(self, entity_id: SymbolID) -> None:
|
|
24
|
+
entity = self.entities.pop(entity_id, None)
|
|
25
|
+
if entity is not None:
|
|
26
|
+
fqn = getattr(entity, "fqn", None)
|
|
27
|
+
if fqn and entity_id in self.fqn_to_ids.get(fqn, set()):
|
|
28
|
+
self.fqn_to_ids[fqn].discard(entity_id)
|
|
29
|
+
if not self.fqn_to_ids[fqn]:
|
|
30
|
+
del self.fqn_to_ids[fqn]
|
|
31
|
+
|
|
32
|
+
def get_ids_by_fqn(self, fqn: str) -> set[SymbolID]:
|
|
33
|
+
return set(self.fqn_to_ids.get(fqn, set()))
|
|
34
|
+
|
|
35
|
+
def values(self):
|
|
36
|
+
return self.entities.values()
|
|
37
|
+
|
|
38
|
+
def get(self, entity_id: SymbolID, default=None):
|
|
39
|
+
return self.entities.get(entity_id, default)
|
|
40
|
+
|
|
41
|
+
def __contains__(self, entity_id: SymbolID) -> bool:
|
|
42
|
+
return entity_id in self.entities
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deproc-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Language-agnostic core contracts and runtime for deproc plugins.
|
|
5
|
+
Author-email: Gurveer Singh Virk <gurveervirk.15517@gmail.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/gurveervirk/deproc
|
|
8
|
+
Project-URL: Repository, https://github.com/gurveervirk/deproc
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Requires-Python: >=3.10
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
deproc/core/__init__.py,sha256=byQSoqypO46cce6J7ykiEgVLzy90xdMvU38k_qigrA0,81
|
|
2
|
+
deproc/core/context.py,sha256=oNRBB2OzXLpYLahe3IyAi2_G4Uu0VSfuiPocBJEoYmU,8283
|
|
3
|
+
deproc/core/discovery.py,sha256=GUxOqSby7tzwGOHhjMN52rPg7gVDEkHMuZMZvSab0PE,743
|
|
4
|
+
deproc/core/interfaces/__init__.py,sha256=fAScn456-iPQ8jhNMV5GcDcM4Hx80R5YJfMR8FQjsPA,209
|
|
5
|
+
deproc/core/interfaces/linker.py,sha256=N1aRQp2L9Alks4_eygKXyxc091atoddt5bJ1wrMzILM,429
|
|
6
|
+
deproc/core/interfaces/resolver.py,sha256=MuAX16ylFqnC12DzjmXglo6NfZZXIEHR3uB--eDlIuo,285
|
|
7
|
+
deproc/core/interfaces/symbol_cache.py,sha256=Ah-FCeAs84OfbgGUm1H5fcBuzQZbnZBOHXEbBz5sfVE,377
|
|
8
|
+
deproc/core/interfaces/parser/__init__.py,sha256=muqAf12npvewkM4cmK38oLyO6SQrn8SCwBjkIatE2jI,64
|
|
9
|
+
deproc/core/interfaces/parser/base.py,sha256=PsbcKn9acd8oa7FR_K_dSJiFUANdITzbHzTLTWwfx18,321
|
|
10
|
+
deproc/core/interfaces/parser/models.py,sha256=kDbbFxBVSjFs-m1_S5uIX4GoxJTjPEpyGS93P6sOnyQ,4147
|
|
11
|
+
deproc/core/runtime/__init__.py,sha256=OQycEB3UA8DMZ2_OBIvjDzxGJubYe48fQAJ2KuqajJM,83
|
|
12
|
+
deproc/core/runtime/registries/__init__.py,sha256=N4VQvTpcvzQTnKM7XvYhnzGqLVONQ69EImfD8K-hX-A,70
|
|
13
|
+
deproc/core/runtime/registries/entity.py,sha256=fMbpIRkJ--t7ig_Q3RP1akQSWw2mHP1e-VMLLQJizgg,1425
|
|
14
|
+
deproc_core-0.1.0.dist-info/METADATA,sha256=y0emwcdJGseLvbkbRBVCOd39aYIYoK37baNfnx_cRtg,870
|
|
15
|
+
deproc_core-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
16
|
+
deproc_core-0.1.0.dist-info/top_level.txt,sha256=qyPIAnJVTLaewuAbLpShmn-777EtCI7b1b5sGgkzO10,7
|
|
17
|
+
deproc_core-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
deproc
|