python-oracle-apex 0.0.1__tar.gz → 0.0.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: python-oracle-apex
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: Parsing files exported from Oracle APEX
5
5
  Author: Maurice Koster
6
6
  Author-email: Maurice Koster <maurice@mauricekoster.com>
@@ -20,6 +20,28 @@ Parsing an APEX lang export file into Objects
20
20
  page = parse_apex("examples/login.apx")
21
21
  ```
22
22
 
23
+ Better:
24
+ ```python
25
+ parser = ApexParser()
26
+ page = parser.parse_file("examples/login.apx")
27
+
28
+ ```
29
+
30
+ Or from a string:
31
+ ```python
32
+ parser = ApexParser()
33
+ content = """page A (
34
+ page: 1000
35
+ name: Hello
36
+ alias: HOME
37
+ title: How the West was won
38
+ )
39
+ """
40
+ page = parser.parse(content)
41
+ assert page.name == "Hello"
42
+
43
+ ```
44
+
23
45
  ## Reading yaml files, pre APEX 26.1
24
46
 
25
47
  ```python
@@ -0,0 +1,38 @@
1
+ # python-oracle-apex
2
+ Parsing an APEX lang export file into Objects
3
+
4
+ ## Oracle APEX
5
+
6
+ ## APEXLang - reading .apx files
7
+
8
+ ```python
9
+ page = parse_apex("examples/login.apx")
10
+ ```
11
+
12
+ Better:
13
+ ```python
14
+ parser = ApexParser()
15
+ page = parser.parse_file("examples/login.apx")
16
+
17
+ ```
18
+
19
+ Or from a string:
20
+ ```python
21
+ parser = ApexParser()
22
+ content = """page A (
23
+ page: 1000
24
+ name: Hello
25
+ alias: HOME
26
+ title: How the West was won
27
+ )
28
+ """
29
+ page = parser.parse(content)
30
+ assert page.name == "Hello"
31
+
32
+ ```
33
+
34
+ ## Reading yaml files, pre APEX 26.1
35
+
36
+ ```python
37
+ page = parse_yaml("examples/login.yaml")
38
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-oracle-apex"
3
- version = "0.0.1"
3
+ version = "0.0.2"
4
4
  description = "Parsing files exported from Oracle APEX"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -21,4 +21,8 @@ requires = ["uv_build>=0.12.7,<0.13.0"]
21
21
  build-backend = "uv_build"
22
22
 
23
23
  [dependency-groups]
24
- dev = ["pytest>=9.1.1"]
24
+ dev = [
25
+ "coverage>=7.16.0",
26
+ "pytest>=9.1.1",
27
+ "pytest-cov>=7.1.0",
28
+ ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-oracle-apex"
3
- version = "0.0.1"
3
+ version = "0.0.2"
4
4
  description = "Parsing files exported from Oracle APEX"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -21,5 +21,7 @@ build-backend = "uv_build"
21
21
 
22
22
  [dependency-groups]
23
23
  dev = [
24
+ "coverage>=7.16.0",
24
25
  "pytest>=9.1.1",
26
+ "pytest-cov>=7.1.0",
25
27
  ]
@@ -0,0 +1,4 @@
1
+ from .parser import parse_apex_file, parse_apex
2
+ from .reader import parse_yaml_file
3
+ from .objects import *
4
+
@@ -1,3 +1,9 @@
1
+ component = page_object blank_lines
2
+ / region blank_lines
3
+ / process blank_lines
4
+
5
+
6
+
1
7
  page_object = indent "page" (required_ws component_id)? ws "(" page_body_line* indent ")" line_end
2
8
  page_body_line = page_direct_property_line
3
9
  / page_group_block
@@ -15,6 +21,7 @@ page_group_block = page_appearance
15
21
  / page_css
16
22
  / page_security
17
23
  / page_advanced
24
+ / page_help
18
25
 
19
26
  page_appearance = indent "appearance" ws "{" line_end page_appearance_property_line* indent "}" line_end
20
27
  page_appearance_property_line = indent page_appearance_property line_end
@@ -53,6 +60,12 @@ page_advanced_property = "enableMetaTags" ":" ws boolean
53
60
  / "duplicateSubmissionUrl" ":" ws string_like_value
54
61
 
55
62
 
63
+ page_help = indent "help" ws "{" line_end page_help_property_line* indent "}" line_end
64
+ page_help_property_line = indent page_help_property line_end
65
+ page_help_property = "helpText" ":" ws code_block
66
+
67
+
68
+
56
69
  page_child_component = region
57
70
  / page_item
58
71
  / button
@@ -270,9 +283,27 @@ process_group_block = process_execution
270
283
  / process_source
271
284
  / process_advanced
272
285
 
273
- process_source = "ERROR"
274
- process_execution = "ERROR"
275
- process_advanced = "ERROR"
286
+ process_source = indent "source" ws "{" line_end process_source_property_line* indent "}" line_end
287
+ process_source_property_line = indent process_source_property line_end
288
+ process_source_property = "location" ":" ws ( "localDatabase" / "restEnabledSql" )
289
+ / "language" ":" ws ( "plsql" / "javaScript-mle" )
290
+ / "plsqlCode" ":" ws code_block
291
+ / "javaScriptCode" ":" ws code_block
292
+ / "remoteServer" ":" ws reference
293
+
294
+
295
+ process_execution = indent "execution" ws "{" line_end process_execution_property_line* indent "}" line_end
296
+ process_execution_property_line = indent process_execution_property line_end
297
+ process_execution_property = "sequence" ":" ws number
298
+ / "point" ":" ws string_like_value
299
+ / "runProcess" ":" ws ( "oncePerSessionOrWhenReset" / "oncePerPageVisitDefault" )
300
+
301
+
302
+ process_advanced = indent "advanced" ws "{" line_end process_advanced_property_line* indent "}" line_end
303
+ process_advanced_property_line = indent process_advanced_property line_end
304
+ process_advanced_property = "staticId" ":" ws string_like_value
305
+ / "executionMappingIdentifier" ":" ws string_like_value
306
+
276
307
 
277
308
 
278
309
  dynamic_action = indent "dynamicAction" (required_ws component_id)? ws "(" line_end dynamic_action_body_line* indent ")" line_end
@@ -361,7 +392,7 @@ action_c_execution_property = "sequence" ":" ws number
361
392
 
362
393
  reference = "@" reference_character*
363
394
  component_id = string / identifier / number
364
- code_block = multiline_string
395
+ code_block = nl? ws multiline_string
365
396
  / string_like_value
366
397
  multiline_string = "```" ( any_character / nl )* "```"
367
398
  blank_lines = (ws nl)*
@@ -227,10 +227,90 @@ class ActionExecution(ApexGroup):
227
227
  class Action(ApexObject):
228
228
  pass
229
229
 
230
+
231
+ class ProcessExecution(ApexGroup):
232
+ @property
233
+ def sequence(self):
234
+ return self['sequence']
235
+
236
+ @property
237
+ def point(self):
238
+ return self['point']
239
+
240
+ @property
241
+ def runProcess(self):
242
+ return self['runProcess']
243
+
244
+
245
+ class ProcessAdvanced(ApexGroup):
246
+ @property
247
+ def staticId(self):
248
+ return self['staticId']
249
+
250
+ @property
251
+ def executionMappingIdentifier(self):
252
+ return self['executionMappingIdentifier']
253
+
254
+ class ProcessSource(ApexGroup):
255
+ @property
256
+ def location(self):
257
+ return self['location']
258
+
259
+ @property
260
+ def language(self):
261
+ return self['language']
262
+
263
+ @property
264
+ def remoteServer(self):
265
+ return self['remoteServer']
266
+
267
+ @property
268
+ def plsqlCode(self):
269
+ return self['plsqlCode']
270
+
230
271
  class Process(ApexObject):
231
272
  def __init__(self, component_id=None, initial_data={}):
232
273
  super().__init__(component_id, initial_data)
233
274
 
275
+ @property
276
+ def name(self):
277
+ return self['name']
278
+
279
+ @property
280
+ def type(self):
281
+ return self['type']
282
+
283
+ @property
284
+ def source(self) -> ProcessSource:
285
+ return self['source']
286
+
287
+
288
+ @property
289
+ def execution(self):
290
+ return self['execution']
291
+
292
+ @property
293
+ def advanced(self):
294
+ return self['advanced']
295
+
296
+
297
+ class PageAppearance(ApexGroup):
298
+ pass
299
+
300
+ class PageNavigation(ApexGroup):
301
+ pass
302
+
303
+ class PageCss(ApexGroup):
304
+ pass
305
+
306
+ class PageSecurity(ApexGroup):
307
+ pass
308
+
309
+ class PageAdvanced(ApexGroup):
310
+ pass
311
+
312
+ class PageHelp(ApexGroup):
313
+ pass
234
314
 
235
315
  class Page(ApexObject):
236
316
  def __init__(self, component_id=None, initial_data={}):
@@ -0,0 +1,47 @@
1
+ from parsimonious import Grammar
2
+ from python_oracle_apex.objects import *
3
+ from importlib import resources as impresources
4
+ from pathlib import Path
5
+
6
+
7
+ import python_oracle_apex
8
+ from .visitor import ApxNodeVisitor
9
+
10
+
11
+ class ApexParser:
12
+ def __init__(self, apex_version : str ="26.1"):
13
+ inp_file = impresources.files(python_oracle_apex) / f'apexlang-{apex_version}.peg'
14
+ with inp_file.open("rt") as f:
15
+ template = f.read()
16
+
17
+ self.grammar = Grammar(template)
18
+ self.visitor = ApxNodeVisitor()
19
+
20
+ def parse(self, data):
21
+ nodes = self.grammar.parse(data)
22
+ output = self.visitor.visit(nodes)
23
+ return output
24
+
25
+ def parse_file(self, apex_file: str | Path):
26
+ if type(apex_file) is str:
27
+ fn = Path(apex_file)
28
+ else:
29
+ fn = apex_file
30
+ with fn.open('r') as f:
31
+ data = f.read()
32
+
33
+ return self.parse(data)
34
+
35
+
36
+ def parse_apex_file(apex_file: str | Path, apex_version : str = "26.1") -> ApexObject:
37
+ return ApexParser(apex_version).parse_file(apex_file)
38
+
39
+ def parse_apex(data: str, apex_version : str = "26.1") -> ApexObject:
40
+ return ApexParser(apex_version).parse(data)
41
+
42
+
43
+
44
+ if __name__ == '__main__': # pragma: no cover
45
+ apex_object = parse_apex_file("examples/test.apx")
46
+
47
+ print(apex_object)
@@ -9,6 +9,10 @@ class RuleNotImplemented(BaseException):
9
9
 
10
10
 
11
11
  class ApxNodeVisitor(NodeVisitor):
12
+
13
+ def visit_component(self, node, visited_children):
14
+ return visited_children[0][0]
15
+
12
16
  def visit_page_object(self, node, visited_children):
13
17
  """ Gets the section name. """
14
18
  component_id = visited_children[2]
@@ -23,6 +27,8 @@ class ApxNodeVisitor(NodeVisitor):
23
27
  if type(item) is tuple:
24
28
  if type(item[1]) is dict:
25
29
  page.add_group(item[0], item[1])
30
+ elif isinstance(item[1], ApexGroup):
31
+ page.add_group(item[0], item[1])
26
32
  else:
27
33
  page.add_property(item[0], item[1])
28
34
  elif isinstance(item, ApexObject):
@@ -63,7 +69,7 @@ class ApxNodeVisitor(NodeVisitor):
63
69
  d[item[0]] = item[1]
64
70
  else:
65
71
  raise RuleNotImplemented()
66
- return ('appearance', d)
72
+ return ('appearance', PageAppearance(d))
67
73
 
68
74
  def visit_page_appearance_property_line(self, node, visited_children):
69
75
  return visited_children[1]
@@ -81,7 +87,7 @@ class ApxNodeVisitor(NodeVisitor):
81
87
  d[item[0]] = item[1]
82
88
  else:
83
89
  raise RuleNotImplemented()
84
- return ('navigation', d)
90
+ return ('navigation', PageNavigation(d))
85
91
 
86
92
  def visit_page_navigation_property_line(self, node, visited_children):
87
93
  return visited_children[1]
@@ -103,7 +109,7 @@ class ApxNodeVisitor(NodeVisitor):
103
109
  d[item[0]] = item[1]
104
110
  else:
105
111
  raise RuleNotImplemented()
106
- return ('css', d)
112
+ return ('css', PageCss(d))
107
113
 
108
114
  def visit_page_css_property_line(self, node, visited_children):
109
115
  return visited_children[1]
@@ -125,7 +131,7 @@ class ApxNodeVisitor(NodeVisitor):
125
131
  d[item[0]] = item[1]
126
132
  else:
127
133
  raise RuleNotImplemented()
128
- return ('security', d)
134
+ return ('security', PageSecurity(d))
129
135
 
130
136
  def visit_page_security_property_line(self, node, visited_children):
131
137
  return visited_children[1]
@@ -149,7 +155,7 @@ class ApxNodeVisitor(NodeVisitor):
149
155
  d[item[0]] = item[1]
150
156
  else:
151
157
  raise RuleNotImplemented()
152
- return ('advanced', d)
158
+ return ('advanced', PageAdvanced(d))
153
159
 
154
160
  def visit_page_advanced_property_line(self, node, visited_children):
155
161
  return visited_children[1]
@@ -165,6 +171,23 @@ class ApxNodeVisitor(NodeVisitor):
165
171
  case _:
166
172
  raise RuleNotImplemented()
167
173
 
174
+ def visit_page_help(self, node, visited_children):
175
+ parts = visited_children[5]
176
+ d = {}
177
+ for item in parts:
178
+ if type(item) is tuple:
179
+ d[item[0]] = item[1]
180
+ else:
181
+ raise RuleNotImplemented()
182
+ return ('help', PageHelp(d))
183
+
184
+ def visit_page_help_property_line(self, node, visited_children):
185
+ return visited_children[1]
186
+
187
+ def visit_page_help_property(self, node, visited_children):
188
+ v = visited_children[0]
189
+ return (v.text, visited_children[3])
190
+
168
191
 
169
192
  def visit_region(self, node, visited_children):
170
193
  component_id = visited_children[2]
@@ -881,12 +904,89 @@ class ApxNodeVisitor(NodeVisitor):
881
904
  return visited_children[0]
882
905
 
883
906
 
907
+ def visit_process_source(self, node, visited_children):
908
+ parts = visited_children[5]
909
+ d = {}
910
+ for item in parts:
911
+ if type(item) is tuple:
912
+ d[item[0]] = item[1]
913
+ else:
914
+ raise RuleNotImplemented()
915
+ return ('source', ProcessSource(d) )
916
+
917
+ def visit_process_source_property_line(self, node, visited_children):
918
+ return visited_children[1]
919
+
920
+ def visit_process_source_property(self, node, visited_children):
921
+ v = visited_children[0]
922
+
923
+ title, _, _, value = v
924
+ match title.text:
925
+ case 'location' | 'language':
926
+ value = value[0].text
927
+ return (title.text, value)
928
+
929
+
930
+
931
+ def visit_process_execution(self, node, visited_children):
932
+ parts = visited_children[5]
933
+ d = {}
934
+ for item in parts:
935
+ if type(item) is tuple:
936
+ d[item[0]] = item[1]
937
+ else:
938
+ raise RuleNotImplemented()
939
+ return ('execution', ProcessExecution(d) )
940
+
941
+ def visit_process_execution_property_line(self, node, visited_children):
942
+ return visited_children[1]
943
+
944
+ def visit_process_execution_property(self, node, visited_children):
945
+ v = visited_children[0]
946
+
947
+ title, _, _, value = v
948
+ if title.text == 'runProcess':
949
+ value = value[0].text
950
+ return (title.text, value)
951
+
952
+
953
+ def visit_process_advanced(self, node, visited_children):
954
+ parts = visited_children[5]
955
+ d = {}
956
+ for item in parts:
957
+ if type(item) is tuple:
958
+ d[item[0]] = item[1]
959
+ else:
960
+ raise RuleNotImplemented()
961
+ return ('advanced', ProcessAdvanced(d) )
962
+
963
+ def visit_process_advanced_property_line(self, node, visited_children):
964
+ return visited_children[1]
965
+
966
+ def visit_process_advanced_property(self, node, visited_children):
967
+ v = visited_children[0]
968
+
969
+ title, _, _, value = v
970
+ return (title.text, value)
971
+
972
+
973
+
884
974
  def visit_reference(self, node, visited_children):
885
975
  return node.text
886
976
 
887
977
 
888
978
  def visit_code_block(self, node, visited_children):
889
- return node.text
979
+ if type(visited_children[0]) is str:
980
+ return visited_children[0]
981
+
982
+ indent_len = len(visited_children[0][1])
983
+ lines = visited_children[0][2].split("\n")
984
+ if indent_len == 0:
985
+ indent_len = lines[-1].find('```')
986
+ code = [lines[0]]
987
+ for line in lines[1:]:
988
+ code.append(line[indent_len:])
989
+ return "\n".join(code)
890
990
 
891
991
  def visit_multiline_string(self, node, visited_children):
892
992
  return node.text
@@ -925,7 +1025,7 @@ class ApxNodeVisitor(NodeVisitor):
925
1025
  return None
926
1026
 
927
1027
  def visit_ws(self, node, visited_children):
928
- return None
1028
+ return node.text
929
1029
 
930
1030
  def visit_blank_lines(self, node, visited_children):
931
1031
  return None
@@ -934,41 +1034,3 @@ class ApxNodeVisitor(NodeVisitor):
934
1034
  def generic_visit(self, node, visited_children):
935
1035
  """ The generic visit method. """
936
1036
  return visited_children or node
937
-
938
- def parse_apex_file(apex_file: str | Path, apex_version : str = "26.1") -> ApexObject:
939
- if type(apex_file) is str:
940
- fn = Path(apex_file)
941
- else:
942
- fn = apex_file
943
- with fn.open('r') as f:
944
- data = f.read()
945
-
946
- return parse_apex(data, apex_version)
947
-
948
- def parse_apex(data: str, apex_version : str = "26.1") -> ApexObject:
949
- inp_file = impresources.files(python_oracle_apex) / f'apexlang-{apex_version}.peg'
950
- with inp_file.open("rt") as f:
951
- template = f.read()
952
-
953
- grammar = Grammar(template)
954
-
955
- nodes = grammar.parse(data)
956
-
957
- visitor = ApxNodeVisitor()
958
- output = visitor.visit(nodes)
959
- return output
960
-
961
- if __name__ == '__main__': # pragma: no cover
962
-
963
-
964
-
965
- page = parse_apex(
966
- """page A (
967
- )
968
- """)
969
-
970
-
971
-
972
- print(page)
973
-
974
- print(page.appearance)
@@ -1,16 +0,0 @@
1
- # python-oracle-apex
2
- Parsing an APEX lang export file into Objects
3
-
4
- ## Oracle APEX
5
-
6
- ## APEXLang - reading .apx files
7
-
8
- ```python
9
- page = parse_apex("examples/login.apx")
10
- ```
11
-
12
- ## Reading yaml files, pre APEX 26.1
13
-
14
- ```python
15
- page = parse_yaml("examples/login.yaml")
16
- ```
@@ -1,6 +0,0 @@
1
- from .grammar import parse_apex_file, parse_apex
2
- from .reader import parse_yaml_file
3
- from .objects import *
4
-
5
- def main() -> None:
6
- print("Hello from python-oracle-apex!")