pydpm_xl 0.1.39rc31__py3-none-any.whl → 0.2.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.
- py_dpm/__init__.py +1 -1
- py_dpm/api/__init__.py +58 -189
- py_dpm/api/dpm/__init__.py +20 -0
- py_dpm/api/{data_dictionary.py → dpm/data_dictionary.py} +903 -984
- py_dpm/api/dpm/explorer.py +236 -0
- py_dpm/api/dpm/hierarchical_queries.py +142 -0
- py_dpm/api/{migration.py → dpm/migration.py} +16 -19
- py_dpm/api/{operation_scopes.py → dpm/operation_scopes.py} +319 -267
- py_dpm/api/dpm_xl/__init__.py +25 -0
- py_dpm/api/{ast_generator.py → dpm_xl/ast_generator.py} +3 -3
- py_dpm/api/{complete_ast.py → dpm_xl/complete_ast.py} +192 -168
- py_dpm/api/dpm_xl/semantic.py +354 -0
- py_dpm/api/{syntax.py → dpm_xl/syntax.py} +6 -5
- py_dpm/api/explorer.py +4 -0
- py_dpm/api/semantic.py +30 -306
- py_dpm/cli/__init__.py +9 -0
- py_dpm/{client.py → cli/main.py} +8 -8
- py_dpm/dpm/__init__.py +11 -0
- py_dpm/{models.py → dpm/models.py} +112 -88
- py_dpm/dpm/queries/base.py +100 -0
- py_dpm/dpm/queries/basic_objects.py +33 -0
- py_dpm/dpm/queries/explorer_queries.py +352 -0
- py_dpm/dpm/queries/filters.py +139 -0
- py_dpm/dpm/queries/glossary.py +45 -0
- py_dpm/dpm/queries/hierarchical_queries.py +838 -0
- py_dpm/dpm/queries/tables.py +133 -0
- py_dpm/dpm/utils.py +356 -0
- py_dpm/dpm_xl/__init__.py +8 -0
- py_dpm/dpm_xl/ast/__init__.py +14 -0
- py_dpm/{AST/ASTConstructor.py → dpm_xl/ast/constructor.py} +6 -6
- py_dpm/{AST/MLGeneration.py → dpm_xl/ast/ml_generation.py} +137 -87
- py_dpm/{AST/ModuleAnalyzer.py → dpm_xl/ast/module_analyzer.py} +7 -7
- py_dpm/{AST/ModuleDependencies.py → dpm_xl/ast/module_dependencies.py} +56 -41
- py_dpm/{AST/ASTObjects.py → dpm_xl/ast/nodes.py} +1 -1
- py_dpm/{AST/check_operands.py → dpm_xl/ast/operands.py} +16 -13
- py_dpm/{AST/ASTTemplate.py → dpm_xl/ast/template.py} +2 -2
- py_dpm/{AST/WhereClauseChecker.py → dpm_xl/ast/where_clause.py} +2 -2
- py_dpm/dpm_xl/grammar/__init__.py +18 -0
- py_dpm/dpm_xl/operators/__init__.py +19 -0
- py_dpm/{Operators/AggregateOperators.py → dpm_xl/operators/aggregate.py} +7 -7
- py_dpm/{Operators/NumericOperators.py → dpm_xl/operators/arithmetic.py} +6 -6
- py_dpm/{Operators/Operator.py → dpm_xl/operators/base.py} +5 -5
- py_dpm/{Operators/BooleanOperators.py → dpm_xl/operators/boolean.py} +5 -5
- py_dpm/{Operators/ClauseOperators.py → dpm_xl/operators/clause.py} +8 -8
- py_dpm/{Operators/ComparisonOperators.py → dpm_xl/operators/comparison.py} +5 -5
- py_dpm/{Operators/ConditionalOperators.py → dpm_xl/operators/conditional.py} +7 -7
- py_dpm/{Operators/StringOperators.py → dpm_xl/operators/string.py} +5 -5
- py_dpm/{Operators/TimeOperators.py → dpm_xl/operators/time.py} +6 -6
- py_dpm/{semantics/SemanticAnalyzer.py → dpm_xl/semantic_analyzer.py} +168 -68
- py_dpm/{semantics/Symbols.py → dpm_xl/symbols.py} +3 -3
- py_dpm/dpm_xl/types/__init__.py +13 -0
- py_dpm/{DataTypes/TypePromotion.py → dpm_xl/types/promotion.py} +2 -2
- py_dpm/{DataTypes/ScalarTypes.py → dpm_xl/types/scalar.py} +2 -2
- py_dpm/dpm_xl/utils/__init__.py +14 -0
- py_dpm/{data_handlers.py → dpm_xl/utils/data_handlers.py} +2 -2
- py_dpm/{Utils → dpm_xl/utils}/operands_mapping.py +1 -1
- py_dpm/{Utils → dpm_xl/utils}/operator_mapping.py +8 -8
- py_dpm/{OperationScopes/OperationScopeService.py → dpm_xl/utils/scopes_calculator.py} +148 -58
- py_dpm/{Utils/ast_serialization.py → dpm_xl/utils/serialization.py} +2 -2
- py_dpm/dpm_xl/validation/__init__.py +12 -0
- py_dpm/{Utils/ValidationsGenerationUtils.py → dpm_xl/validation/generation_utils.py} +2 -3
- py_dpm/{ValidationsGeneration/PropertiesConstraintsProcessor.py → dpm_xl/validation/property_constraints.py} +56 -21
- py_dpm/{ValidationsGeneration/auxiliary_functions.py → dpm_xl/validation/utils.py} +2 -2
- py_dpm/{ValidationsGeneration/VariantsProcessor.py → dpm_xl/validation/variants.py} +149 -55
- py_dpm/exceptions/__init__.py +23 -0
- py_dpm/{Exceptions → exceptions}/exceptions.py +7 -2
- pydpm_xl-0.2.0.dist-info/METADATA +278 -0
- pydpm_xl-0.2.0.dist-info/RECORD +88 -0
- pydpm_xl-0.2.0.dist-info/entry_points.txt +2 -0
- py_dpm/Exceptions/__init__.py +0 -0
- py_dpm/OperationScopes/__init__.py +0 -0
- py_dpm/Operators/__init__.py +0 -0
- py_dpm/Utils/__init__.py +0 -0
- py_dpm/Utils/utils.py +0 -2
- py_dpm/ValidationsGeneration/Utils.py +0 -364
- py_dpm/ValidationsGeneration/__init__.py +0 -0
- py_dpm/api/data_dictionary_validation.py +0 -614
- py_dpm/db_utils.py +0 -221
- py_dpm/grammar/__init__.py +0 -0
- py_dpm/grammar/dist/__init__.py +0 -0
- py_dpm/grammar/dpm_xlLexer.g4 +0 -437
- py_dpm/grammar/dpm_xlParser.g4 +0 -263
- py_dpm/semantics/DAG/DAGAnalyzer.py +0 -158
- py_dpm/semantics/DAG/__init__.py +0 -0
- py_dpm/semantics/__init__.py +0 -0
- py_dpm/views/data_types.sql +0 -12
- py_dpm/views/datapoints.sql +0 -65
- py_dpm/views/hierarchy_operand_reference.sql +0 -11
- py_dpm/views/hierarchy_preconditions.sql +0 -13
- py_dpm/views/hierarchy_variables.sql +0 -26
- py_dpm/views/hierarchy_variables_context.sql +0 -14
- py_dpm/views/key_components.sql +0 -18
- py_dpm/views/module_from_table.sql +0 -11
- py_dpm/views/open_keys.sql +0 -13
- py_dpm/views/operation_info.sql +0 -27
- py_dpm/views/operation_list.sql +0 -18
- py_dpm/views/operations_versions_from_module_version.sql +0 -30
- py_dpm/views/precondition_info.sql +0 -17
- py_dpm/views/report_type_operand_reference_info.sql +0 -18
- py_dpm/views/subcategory_info.sql +0 -17
- py_dpm/views/table_info.sql +0 -19
- pydpm_xl-0.1.39rc31.dist-info/METADATA +0 -53
- pydpm_xl-0.1.39rc31.dist-info/RECORD +0 -96
- pydpm_xl-0.1.39rc31.dist-info/entry_points.txt +0 -2
- /py_dpm/{AST → cli/commands}/__init__.py +0 -0
- /py_dpm/{migration.py → dpm/migration.py} +0 -0
- /py_dpm/{AST/ASTVisitor.py → dpm_xl/ast/visitor.py} +0 -0
- /py_dpm/{DataTypes → dpm_xl/grammar/generated}/__init__.py +0 -0
- /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlLexer.interp +0 -0
- /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlLexer.py +0 -0
- /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlLexer.tokens +0 -0
- /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParser.interp +0 -0
- /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParser.py +0 -0
- /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParser.tokens +0 -0
- /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParserListener.py +0 -0
- /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParserVisitor.py +0 -0
- /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/listeners.py +0 -0
- /py_dpm/{DataTypes/TimeClasses.py → dpm_xl/types/time.py} +0 -0
- /py_dpm/{Utils → dpm_xl/utils}/tokens.py +0 -0
- /py_dpm/{Exceptions → exceptions}/messages.py +0 -0
- {pydpm_xl-0.1.39rc31.dist-info → pydpm_xl-0.2.0.dist-info}/WHEEL +0 -0
- {pydpm_xl-0.1.39rc31.dist-info → pydpm_xl-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {pydpm_xl-0.1.39rc31.dist-info → pydpm_xl-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
# Import directly to avoid circular imports
|
|
5
|
+
from antlr4 import CommonTokenStream, InputStream
|
|
6
|
+
from py_dpm.dpm_xl.grammar.generated.dpm_xlLexer import dpm_xlLexer
|
|
7
|
+
from py_dpm.dpm_xl.grammar.generated.dpm_xlParser import dpm_xlParser
|
|
8
|
+
from py_dpm.dpm_xl.grammar.generated.listeners import DPMErrorListener
|
|
9
|
+
from py_dpm.dpm_xl.ast.constructor import ASTVisitor
|
|
10
|
+
from py_dpm.dpm_xl.ast.operands import OperandsChecking
|
|
11
|
+
from py_dpm.dpm_xl import semantic_analyzer as SemanticAnalyzer
|
|
12
|
+
from py_dpm.dpm.utils import get_session, get_engine
|
|
13
|
+
from py_dpm.exceptions.exceptions import SemanticError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class SemanticValidationResult:
|
|
18
|
+
"""
|
|
19
|
+
Result of semantic validation.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
is_valid (bool): Whether the semantic validation passed
|
|
23
|
+
error_message (Optional[str]): Error message if validation failed
|
|
24
|
+
error_code (Optional[str]): Error code if validation failed
|
|
25
|
+
expression (str): The original expression that was validated
|
|
26
|
+
validation_type (str): Type of validation performed
|
|
27
|
+
results (Optional[Any]): Additional results from semantic analysis
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
is_valid: bool
|
|
31
|
+
error_message: Optional[str]
|
|
32
|
+
error_code: Optional[str]
|
|
33
|
+
expression: str
|
|
34
|
+
validation_type: str
|
|
35
|
+
results: Optional[Any] = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SemanticAPI:
|
|
39
|
+
"""
|
|
40
|
+
API for DPM-XL semantic validation and analysis.
|
|
41
|
+
|
|
42
|
+
This class provides methods to perform semantic analysis on DPM-XL expressions,
|
|
43
|
+
including operand checking, data type validation, and structure validation.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self, database_path: Optional[str] = None, connection_url: Optional[str] = None
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
Initialize the Semantic API.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
database_path (Optional[str]): Path to SQLite database. If None, uses default from environment.
|
|
54
|
+
connection_url (Optional[str]): Full SQLAlchemy connection URL (e.g., postgresql://user:pass@host:port/db).
|
|
55
|
+
Takes precedence over database_path.
|
|
56
|
+
"""
|
|
57
|
+
self.database_path = database_path
|
|
58
|
+
self.connection_url = connection_url
|
|
59
|
+
|
|
60
|
+
if connection_url:
|
|
61
|
+
# Create isolated engine and session for the provided connection URL
|
|
62
|
+
from sqlalchemy.orm import sessionmaker
|
|
63
|
+
from py_dpm.dpm.utils import create_engine_from_url
|
|
64
|
+
|
|
65
|
+
# Create engine for the connection URL (supports SQLite, PostgreSQL, MySQL, etc.)
|
|
66
|
+
self.engine = create_engine_from_url(connection_url)
|
|
67
|
+
session_maker = sessionmaker(bind=self.engine)
|
|
68
|
+
self.session = session_maker()
|
|
69
|
+
|
|
70
|
+
elif database_path:
|
|
71
|
+
# Create isolated engine and session for this specific database
|
|
72
|
+
from sqlalchemy import create_engine
|
|
73
|
+
from sqlalchemy.orm import sessionmaker
|
|
74
|
+
import os
|
|
75
|
+
|
|
76
|
+
# Create the database directory if it doesn't exist
|
|
77
|
+
db_dir = os.path.dirname(database_path)
|
|
78
|
+
if db_dir and not os.path.exists(db_dir):
|
|
79
|
+
os.makedirs(db_dir)
|
|
80
|
+
|
|
81
|
+
# Create engine for specific database path
|
|
82
|
+
db_connection_url = f"sqlite:///{database_path}"
|
|
83
|
+
self.engine = create_engine(db_connection_url, pool_pre_ping=True)
|
|
84
|
+
session_maker = sessionmaker(bind=self.engine)
|
|
85
|
+
self.session = session_maker()
|
|
86
|
+
else:
|
|
87
|
+
# Use default global connection
|
|
88
|
+
get_engine()
|
|
89
|
+
self.session = get_session()
|
|
90
|
+
self.engine = None
|
|
91
|
+
|
|
92
|
+
self.error_listener = DPMErrorListener()
|
|
93
|
+
self.visitor = ASTVisitor()
|
|
94
|
+
|
|
95
|
+
def validate_expression(
|
|
96
|
+
self, expression: str, release_id: Optional[int] = None
|
|
97
|
+
) -> SemanticValidationResult:
|
|
98
|
+
"""
|
|
99
|
+
Perform semantic validation on a DPM-XL expression.
|
|
100
|
+
|
|
101
|
+
This includes syntax validation, operands checking, data type validation,
|
|
102
|
+
and structure validation.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
expression (str): The DPM-XL expression to validate
|
|
106
|
+
release_id (Optional[int]): Specific release ID for component filtering.
|
|
107
|
+
If None, uses live/latest release (EndReleaseID IS NULL).
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
SemanticValidationResult: Result containing validation status and details
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
>>> from pydpm.api import SemanticAPI
|
|
114
|
+
>>> semantic = SemanticAPI()
|
|
115
|
+
>>> result = semantic.validate_expression("{tC_01.00, r0100, c0010} + {tC_01.00, r0200, c0010}")
|
|
116
|
+
>>> print(result.is_valid)
|
|
117
|
+
True
|
|
118
|
+
|
|
119
|
+
>>> # Validate for specific release
|
|
120
|
+
>>> result = semantic.validate_expression("{tC_01.00, r0100, c0010}", release_id=5)
|
|
121
|
+
"""
|
|
122
|
+
try:
|
|
123
|
+
# Parse expression to AST
|
|
124
|
+
input_stream = InputStream(expression)
|
|
125
|
+
lexer = dpm_xlLexer(input_stream)
|
|
126
|
+
lexer._listeners = [self.error_listener]
|
|
127
|
+
token_stream = CommonTokenStream(lexer)
|
|
128
|
+
|
|
129
|
+
parser = dpm_xlParser(token_stream)
|
|
130
|
+
parser._listeners = [self.error_listener]
|
|
131
|
+
parse_tree = parser.start()
|
|
132
|
+
|
|
133
|
+
if parser._syntaxErrors > 0:
|
|
134
|
+
return SemanticValidationResult(
|
|
135
|
+
is_valid=False,
|
|
136
|
+
error_message="Syntax errors detected",
|
|
137
|
+
error_code="SYNTAX_ERROR",
|
|
138
|
+
expression=expression,
|
|
139
|
+
validation_type="SEMANTIC",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Generate AST
|
|
143
|
+
ast = self.visitor.visit(parse_tree)
|
|
144
|
+
|
|
145
|
+
# Perform semantic analysis
|
|
146
|
+
oc = OperandsChecking(
|
|
147
|
+
session=self.session,
|
|
148
|
+
expression=expression,
|
|
149
|
+
ast=ast,
|
|
150
|
+
release_id=release_id,
|
|
151
|
+
)
|
|
152
|
+
semanticAnalysis = SemanticAnalyzer.InputAnalyzer(expression)
|
|
153
|
+
|
|
154
|
+
semanticAnalysis.data = oc.data
|
|
155
|
+
semanticAnalysis.key_components = oc.key_components
|
|
156
|
+
semanticAnalysis.open_keys = oc.open_keys
|
|
157
|
+
semanticAnalysis.preconditions = oc.preconditions
|
|
158
|
+
|
|
159
|
+
results = semanticAnalysis.visit(ast)
|
|
160
|
+
|
|
161
|
+
return SemanticValidationResult(
|
|
162
|
+
is_valid=True,
|
|
163
|
+
error_message=None,
|
|
164
|
+
error_code=None,
|
|
165
|
+
expression=expression,
|
|
166
|
+
validation_type="SEMANTIC",
|
|
167
|
+
results=results,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
except SemanticError as e:
|
|
171
|
+
return SemanticValidationResult(
|
|
172
|
+
is_valid=False,
|
|
173
|
+
error_message=str(e),
|
|
174
|
+
error_code=getattr(e, "code", None),
|
|
175
|
+
expression=expression,
|
|
176
|
+
validation_type="SEMANTIC",
|
|
177
|
+
)
|
|
178
|
+
except Exception as e:
|
|
179
|
+
return SemanticValidationResult(
|
|
180
|
+
is_valid=False,
|
|
181
|
+
error_message=str(e),
|
|
182
|
+
error_code="UNKNOWN",
|
|
183
|
+
expression=expression,
|
|
184
|
+
validation_type="SEMANTIC",
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def analyze_expression(
|
|
188
|
+
self, expression: str, release_id: Optional[int] = None
|
|
189
|
+
) -> Dict[str, Any]:
|
|
190
|
+
"""
|
|
191
|
+
Perform detailed semantic analysis on a DPM-XL expression.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
expression (str): The DPM-XL expression to analyze
|
|
195
|
+
release_id (Optional[int]): Specific release ID for component filtering.
|
|
196
|
+
If None, uses live/latest release.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Dict[str, Any]: Detailed analysis results
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
Exception: If analysis fails
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
>>> from pydpm.api import SemanticAPI
|
|
206
|
+
>>> semantic = SemanticAPI()
|
|
207
|
+
>>> analysis = semantic.analyze_expression("{tC_01.00, r0100, c0010}")
|
|
208
|
+
>>> # Analyze for specific release
|
|
209
|
+
>>> analysis = semantic.analyze_expression("{tC_01.00, r0100, c0010}", release_id=5)
|
|
210
|
+
"""
|
|
211
|
+
result = self.validate_expression(expression, release_id=release_id)
|
|
212
|
+
|
|
213
|
+
if not result.is_valid:
|
|
214
|
+
raise Exception(f"Semantic analysis failed: {result.error_message}")
|
|
215
|
+
|
|
216
|
+
# Extract additional analysis information
|
|
217
|
+
analysis = {
|
|
218
|
+
"expression": expression,
|
|
219
|
+
"is_valid": True,
|
|
220
|
+
"results": result.results,
|
|
221
|
+
"data_types": (
|
|
222
|
+
getattr(result.results, "type", None) if result.results else None
|
|
223
|
+
),
|
|
224
|
+
"components": (
|
|
225
|
+
getattr(result.results, "components", None) if result.results else None
|
|
226
|
+
),
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return analysis
|
|
230
|
+
|
|
231
|
+
def is_valid_semantics(
|
|
232
|
+
self, expression: str, release_id: Optional[int] = None
|
|
233
|
+
) -> bool:
|
|
234
|
+
"""
|
|
235
|
+
Quick check if expression has valid semantics.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
expression (str): The DPM-XL expression to check
|
|
239
|
+
release_id (Optional[int]): Specific release ID for component filtering.
|
|
240
|
+
If None, uses live/latest release.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
bool: True if semantics are valid, False otherwise
|
|
244
|
+
|
|
245
|
+
Example:
|
|
246
|
+
>>> from pydpm.api import SemanticAPI
|
|
247
|
+
>>> semantic = SemanticAPI()
|
|
248
|
+
>>> is_valid = semantic.is_valid_semantics("{tC_01.00, r0100, c0010}")
|
|
249
|
+
>>> # Check for specific release
|
|
250
|
+
>>> is_valid = semantic.is_valid_semantics("{tC_01.00, r0100, c0010}", release_id=5)
|
|
251
|
+
"""
|
|
252
|
+
result = self.validate_expression(expression, release_id=release_id)
|
|
253
|
+
return result.is_valid
|
|
254
|
+
|
|
255
|
+
def __del__(self):
|
|
256
|
+
"""Clean up resources."""
|
|
257
|
+
try:
|
|
258
|
+
if hasattr(self, "session") and self.session:
|
|
259
|
+
self.session.close()
|
|
260
|
+
except Exception:
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
try:
|
|
264
|
+
if hasattr(self, "engine") and self.engine is not None:
|
|
265
|
+
self.engine.dispose()
|
|
266
|
+
except Exception:
|
|
267
|
+
pass
|
|
268
|
+
|
|
269
|
+
def close(self):
|
|
270
|
+
"""
|
|
271
|
+
Explicitly close the underlying SQLAlchemy session and dispose any private engine.
|
|
272
|
+
"""
|
|
273
|
+
try:
|
|
274
|
+
if hasattr(self, "session") and self.session:
|
|
275
|
+
self.session.close()
|
|
276
|
+
except Exception:
|
|
277
|
+
pass
|
|
278
|
+
|
|
279
|
+
try:
|
|
280
|
+
if hasattr(self, "engine") and self.engine is not None:
|
|
281
|
+
self.engine.dispose()
|
|
282
|
+
except Exception:
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
def __enter__(self):
|
|
286
|
+
return self
|
|
287
|
+
|
|
288
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
289
|
+
self.close()
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# Convenience functions for direct usage
|
|
293
|
+
def validate_expression(
|
|
294
|
+
expression: str,
|
|
295
|
+
database_path: Optional[str] = None,
|
|
296
|
+
connection_url: Optional[str] = None,
|
|
297
|
+
release_id: Optional[int] = None,
|
|
298
|
+
) -> SemanticValidationResult:
|
|
299
|
+
"""
|
|
300
|
+
Convenience function to validate DPM-XL expression semantics.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
expression (str): The DPM-XL expression to validate
|
|
304
|
+
database_path (Optional[str]): Path to SQLite database. If None, uses default from environment.
|
|
305
|
+
connection_url (Optional[str]): Full SQLAlchemy connection URL (e.g., postgresql://user:pass@host:port/db).
|
|
306
|
+
release_id (Optional[int]): Specific release ID for component filtering.
|
|
307
|
+
If None, uses live/latest release.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
SemanticValidationResult: Result containing validation status and details
|
|
311
|
+
|
|
312
|
+
Example:
|
|
313
|
+
>>> from pydpm.api.semantic import validate_expression
|
|
314
|
+
>>> result = validate_expression("{tC_01.00, r0100, c0010}", database_path="./database.db")
|
|
315
|
+
>>> # Using PostgreSQL
|
|
316
|
+
>>> result = validate_expression("{tC_01.00, r0100, c0010}",
|
|
317
|
+
... connection_url="postgresql://user:pass@host:5432/db")
|
|
318
|
+
>>> # Validate for specific release
|
|
319
|
+
>>> result = validate_expression("{tC_01.00, r0100, c0010}", database_path="./database.db", release_id=5)
|
|
320
|
+
"""
|
|
321
|
+
api = SemanticAPI(database_path=database_path, connection_url=connection_url)
|
|
322
|
+
return api.validate_expression(expression, release_id=release_id)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def is_valid_semantics(
|
|
326
|
+
expression: str,
|
|
327
|
+
database_path: Optional[str] = None,
|
|
328
|
+
connection_url: Optional[str] = None,
|
|
329
|
+
release_id: Optional[int] = None,
|
|
330
|
+
) -> bool:
|
|
331
|
+
"""
|
|
332
|
+
Convenience function to check if expression has valid semantics.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
expression (str): The DPM-XL expression to check
|
|
336
|
+
database_path (Optional[str]): Path to SQLite database. If None, uses default from environment.
|
|
337
|
+
connection_url (Optional[str]): Full SQLAlchemy connection URL (e.g., postgresql://user:pass@host:port/db).
|
|
338
|
+
release_id (Optional[int]): Specific release ID for component filtering.
|
|
339
|
+
If None, uses live/latest release.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
bool: True if semantics are valid, False otherwise
|
|
343
|
+
|
|
344
|
+
Example:
|
|
345
|
+
>>> from pydpm.api.semantic import is_valid_semantics
|
|
346
|
+
>>> is_valid = is_valid_semantics("{tC_01.00, r0100, c0010}", database_path="./database.db")
|
|
347
|
+
>>> # Using PostgreSQL
|
|
348
|
+
>>> is_valid = is_valid_semantics("{tC_01.00, r0100, c0010}",
|
|
349
|
+
... connection_url="postgresql://user:pass@host:5432/db")
|
|
350
|
+
>>> # Check for specific release
|
|
351
|
+
>>> is_valid = is_valid_semantics("{tC_01.00, r0100, c0010}", database_path="./database.db", release_id=5)
|
|
352
|
+
"""
|
|
353
|
+
api = SemanticAPI(database_path=database_path, connection_url=connection_url)
|
|
354
|
+
return api.is_valid_semantics(expression, release_id=release_id)
|
|
@@ -3,10 +3,10 @@ from dataclasses import dataclass
|
|
|
3
3
|
|
|
4
4
|
# Import directly to avoid circular imports
|
|
5
5
|
from antlr4 import CommonTokenStream, InputStream
|
|
6
|
-
from py_dpm.grammar.
|
|
7
|
-
from py_dpm.grammar.
|
|
8
|
-
from py_dpm.grammar.
|
|
9
|
-
from py_dpm.
|
|
6
|
+
from py_dpm.dpm_xl.grammar.generated.dpm_xlLexer import dpm_xlLexer
|
|
7
|
+
from py_dpm.dpm_xl.grammar.generated.dpm_xlParser import dpm_xlParser
|
|
8
|
+
from py_dpm.dpm_xl.grammar.generated.listeners import DPMErrorListener
|
|
9
|
+
from py_dpm.dpm_xl.ast.constructor import ASTVisitor
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@dataclass
|
|
@@ -123,7 +123,7 @@ class SyntaxAPI:
|
|
|
123
123
|
|
|
124
124
|
def is_valid_syntax(self, expression: str) -> bool:
|
|
125
125
|
"""
|
|
126
|
-
|
|
126
|
+
Quick check if expression has valid syntax.
|
|
127
127
|
|
|
128
128
|
Args:
|
|
129
129
|
expression (str): The DPM-XL expression to check
|
|
@@ -180,3 +180,4 @@ def is_valid_syntax(expression: str) -> bool:
|
|
|
180
180
|
"""
|
|
181
181
|
api = SyntaxAPI()
|
|
182
182
|
return api.is_valid_syntax(expression)
|
|
183
|
+
|
py_dpm/api/explorer.py
ADDED