kodexa 7.0.12396164812__tar.gz → 7.0.12397101638__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.
Files changed (49) hide show
  1. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/PKG-INFO +1 -1
  2. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/model.py +10 -15
  3. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/persistence.py +3 -1
  4. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/ast.py +24 -18
  5. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/pyproject.toml +1 -1
  6. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/LICENSE +0 -0
  7. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/README.md +0 -0
  8. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/__init__.py +0 -0
  9. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/assistant/__init__.py +0 -0
  10. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/assistant/assistant.py +0 -0
  11. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/connectors/__init__.py +0 -0
  12. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/connectors/connectors.py +0 -0
  13. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/dataclasses/__init__.py +0 -0
  14. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/dataclasses/templates/llm_data_class.j2 +0 -0
  15. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/__init__.py +0 -0
  16. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/base.py +0 -0
  17. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/entities/__init__.py +0 -0
  18. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/entities/check_response.py +0 -0
  19. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/entities/product.py +0 -0
  20. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/entities/product_subscription.py +0 -0
  21. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/objects.py +0 -0
  22. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/model/utils.py +0 -0
  23. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/pipeline/__init__.py +0 -0
  24. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/pipeline/pipeline.py +0 -0
  25. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/platform/__init__.py +0 -0
  26. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/platform/client.py +0 -0
  27. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/platform/interaction.py +0 -0
  28. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/platform/kodexa.py +0 -0
  29. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/__init__.py +0 -0
  30. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/core.py +0 -0
  31. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/lexrules.py +0 -0
  32. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/lextab.py +0 -0
  33. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/lextab.pyi +0 -0
  34. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/parserules.py +0 -0
  35. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/parserules.pyi +0 -0
  36. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/parsetab.py +0 -0
  37. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/selectors/parsetab.pyi +0 -0
  38. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/spatial/__init__.py +0 -0
  39. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/spatial/azure_models.py +0 -0
  40. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/spatial/bbox_common.py +0 -0
  41. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/spatial/table_form_common.py +0 -0
  42. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/steps/__init__.py +0 -0
  43. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/steps/common.py +0 -0
  44. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/testing/__init__.py +0 -0
  45. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/testing/test_components.py +0 -0
  46. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/testing/test_utils.py +0 -0
  47. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/training/__init__.py +0 -0
  48. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/training/train_utils.py +0 -0
  49. {kodexa-7.0.12396164812 → kodexa-7.0.12397101638}/kodexa/utils/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kodexa
3
- Version: 7.0.12396164812
3
+ Version: 7.0.12397101638
4
4
  Summary: Python SDK for the Kodexa Platform
5
5
  Author: Austin Redenbaugh
6
6
  Author-email: austin@kodexa.com
@@ -293,8 +293,6 @@ class ContentNode(object):
293
293
  if content is not None and len(self.get_content_parts()) == 0:
294
294
  self.set_content_parts([content])
295
295
 
296
- self.cached_all_content = None
297
-
298
296
  def get_content_parts(self):
299
297
  return self.document.get_persistence().get_content_parts(self)
300
298
 
@@ -793,12 +791,13 @@ class ContentNode(object):
793
791
  result = self.select(selector, variables)
794
792
  return result[0] if len(result) > 0 else None
795
793
 
796
- def select(self, selector, variables=None):
794
+ def select(self, selector, variables=None, first_only=False):
797
795
  """Select and return the child nodes of this node that match the selector value.
798
796
 
799
797
  Args:
800
798
  selector (str): The selector (ie. //*)
801
799
  variables (dict, optional): A dictionary of variable name/value to use in substituion; defaults to None. Dictionary keys should match a variable specified in the selector.
800
+ first_only (bool, optional): If True, only the first matching node will be returned; defaults to False.
802
801
 
803
802
  Returns:
804
803
  list[ContentNode]: A list of the matching content nodes. If no matches are found, the list will be empty.
@@ -815,18 +814,17 @@ class ContentNode(object):
815
814
  from kodexa.selectors import parse
816
815
  from kodexa.selectors.ast import SelectorContext
817
816
 
818
- context = SelectorContext(self.document)
817
+ context = SelectorContext(self.document, first_only=first_only)
819
818
  parsed_selector = parse(selector)
820
819
  self.document.get_persistence().flush_cache()
821
820
  return parsed_selector.resolve(self, variables, context)
822
821
 
823
- def get_all_content(self, separator=" ", strip=True, use_cache=False):
822
+ def get_all_content(self, separator=" ", strip=True):
824
823
  """Get this node's content, concatenated with all of its children's content.
825
824
 
826
825
  Args:
827
826
  separator(str, optional): The separator to use in joining content together; defaults to " ".
828
827
  strip(boolean, optional): Strip the result
829
- use_cache(boolean, optional): Use the cache
830
828
 
831
829
  Returns:
832
830
  str: The complete content for this node concatenated with the content of all child nodes.
@@ -836,9 +834,6 @@ class ContentNode(object):
836
834
  "This string is made up of multiple nodes"
837
835
  """
838
836
  s = ""
839
- if self.cached_all_content is not None and use_cache:
840
- return self.cached_all_content
841
-
842
837
  children = self.get_content_parts()
843
838
  for part in children:
844
839
  if isinstance(part, str):
@@ -861,14 +856,13 @@ class ContentNode(object):
861
856
  s += separator
862
857
  s += child.get_all_content(separator, strip=strip)
863
858
 
864
- self.cached_all_content = s.strip() if strip else s
865
- return self.cached_all_content
859
+ return s.strip() if strip else s
866
860
 
867
861
  def adopt_children(self, nodes_to_adopt, replace=False):
868
862
  """This will take a list of content nodes and adopt them under this node, ensuring they are re-parented.
869
863
 
870
864
  Args:
871
- children (List[ContentNode]): A list of ContentNodes that will be added to the end of this node's children collection
865
+ nodes_to_adopt (List[ContentNode]): A list of ContentNodes that will be added to the end of this node's children collection
872
866
  replace (bool): If True, will remove all current children and replace them with the new list; defaults to True
873
867
 
874
868
  >>> # select all nodes of type 'line', then the root node 'adopts' them
@@ -3109,17 +3103,18 @@ class Document(object):
3109
3103
  >>> document.get_root().select_first('//*[hasTag($tagName)]', {"tagName": "div"})
3110
3104
  ContentNode
3111
3105
  """
3112
- result = self.select(selector, variables)
3106
+ result = self.select(selector, variables, first_only=True)
3113
3107
  return result[0] if len(result) > 0 else None
3114
3108
 
3115
3109
  def select(
3116
- self, selector: str, variables: Optional[dict] = None
3110
+ self, selector: str, variables: Optional[dict] = None, first_only=False
3117
3111
  ) -> List[ContentNode]:
3118
3112
  """Execute a selector on the root node and then return a list of the matching nodes.
3119
3113
 
3120
3114
  Args:
3121
3115
  selector (str): The selector (ie. //*)
3122
3116
  variables (Optional[dict): A dictionary of variable name/value to use in substituion; defaults to an empty
3117
+ first_only (bool): If True, only the first matching node is returned; defaults to False.
3123
3118
  dictionary. Dictionary keys should match a variable specified in the selector.
3124
3119
 
3125
3120
  Returns:
@@ -3131,7 +3126,7 @@ class Document(object):
3131
3126
  if variables is None:
3132
3127
  variables = {}
3133
3128
  if self.content_node:
3134
- result = self.content_node.select(selector, variables)
3129
+ result = self.content_node.select(selector, variables, first_only)
3135
3130
  if isinstance(result, list):
3136
3131
  return result
3137
3132
 
@@ -96,6 +96,7 @@ class SqliteDocumentPersistence(object):
96
96
  self.cursor.execute("PRAGMA journal_mode=OFF")
97
97
  self.cursor.execute("pragma temp_store = memory")
98
98
  self.cursor.execute("pragma mmap_size = 30000000000")
99
+ self.cursor.execute("pragma cache_size = 10000") # Set the cache size to 10,000 pages
99
100
 
100
101
  try:
101
102
  # We need to populate node_type_id_by_name
@@ -835,9 +836,10 @@ class SqliteDocumentPersistence(object):
835
836
  self.connection.commit()
836
837
  self.cursor.execute("VACUUM")
837
838
  self.cursor = self.connection.cursor()
838
- self.cursor.execute("PRAGMA journal_mode=OFF")
839
+ self.cursor.execute("pragma journal_mode=OFF")
839
840
  self.cursor.execute("pragma temp_store = memory")
840
841
  self.cursor.execute("pragma mmap_size = 30000000000")
842
+ self.cursor.execute("pragma cache_size = 10000") # Set the cache size to 10,000 pages
841
843
 
842
844
  def dump_in_memory_db_to_file(self):
843
845
  # Connect to a new or existing database file
@@ -36,11 +36,12 @@ __all__ = [
36
36
 
37
37
 
38
38
  class SelectorContext:
39
- def __init__(self, document: Document):
39
+ def __init__(self, document: Document, first_only=False):
40
40
  self.pattern_cache = {}
41
41
  self.last_op = None
42
42
  self.document: Document = document
43
43
  self.stream = 0
44
+ self.first_only = first_only
44
45
 
45
46
  def cache_pattern(self, pattern):
46
47
  if pattern not in self.pattern_cache:
@@ -53,20 +54,26 @@ class PipelineExpression(object):
53
54
 
54
55
  def __init__(self, left, op, right):
55
56
  self.left = left
56
- """the left side of the pipeline expression"""
57
57
  self.op = op
58
- """the operator of the pipeline expression"""
59
58
  self.right = right
60
- """the right side of the pipeline expression"""
61
59
 
62
60
  def resolve(self, content_node: ContentNode, variables, context: SelectorContext):
63
61
  left_nodes = self.left.resolve(content_node, variables, context)
64
62
  result_nodes: List[ContentNode] = []
65
63
  context.stream = context.stream + 1
66
- for node in left_nodes:
67
- result_nodes.extend(self.right.resolve(node, variables, context))
64
+
65
+ # If first_only is True and we already have left nodes, only process the first one
66
+ nodes_to_process = left_nodes[:1] if context.first_only and left_nodes else left_nodes
67
+
68
+ for node in nodes_to_process:
69
+ right_results = self.right.resolve(node, variables, context)
70
+ result_nodes.extend(right_results)
71
+ # If first_only is True and we found a match, return immediately
72
+ if context.first_only and result_nodes:
73
+ break
74
+
68
75
  context.stream = context.stream - 1
69
- return result_nodes
76
+ return result_nodes[:1] if context.first_only else result_nodes
70
77
 
71
78
 
72
79
  class UnaryExpression(object):
@@ -181,17 +188,12 @@ class AbsolutePath(object):
181
188
 
182
189
 
183
190
  class Step(object):
184
- """
185
- A single step in a relative path. a; @b; text(); parent::foo:bar[5].
186
- """
191
+ """A single step in a relative path."""
187
192
 
188
193
  def __init__(self, axis, node_test, predicates):
189
194
  self.axis = axis
190
- """the step's axis, or @ or None if abbreviated or undefined"""
191
195
  self.node_test = node_test
192
- """a NameTest or NodeType object describing the test represented"""
193
196
  self.predicates = predicates
194
- """a list of predicates filtering the step"""
195
197
 
196
198
  def resolve(self, obj, variables, context: SelectorContext):
197
199
  match = True
@@ -217,8 +219,9 @@ class Step(object):
217
219
  return []
218
220
 
219
221
  nodes = self.node_test.test(axis_node, variables, context)
220
-
221
222
  final_nodes = []
223
+
224
+ # If first_only is True, only process until we find the first match
222
225
  for node in nodes:
223
226
  match = True
224
227
  for predicate in self.predicates:
@@ -230,6 +233,8 @@ class Step(object):
230
233
 
231
234
  if match:
232
235
  final_nodes.append(node)
236
+ if context.first_only:
237
+ break
233
238
 
234
239
  return final_nodes
235
240
 
@@ -247,9 +252,7 @@ class NameTest(object):
247
252
 
248
253
  def __init__(self, prefix, name):
249
254
  self.prefix = prefix
250
- """the namespace prefix used for the test, or None if unset"""
251
255
  self.name = name
252
- """the node name used for the test, or *"""
253
256
 
254
257
  def test(self, obj, variables, context: SelectorContext):
255
258
  if isinstance(obj, ContentNode):
@@ -257,12 +260,15 @@ class NameTest(object):
257
260
  if self.name == "*" or self.name == obj.node_type:
258
261
  return [obj]
259
262
  else:
260
- return context.document.get_persistence().get_content_nodes(
263
+ nodes = context.document.get_persistence().get_content_nodes(
261
264
  self.name, obj, context.last_op != "/"
262
265
  )
266
+ # If first_only is True, return only the first matching node
267
+ return nodes[:1] if context.first_only else nodes
268
+
263
269
  if isinstance(obj, ContentFeature):
264
270
  return self.name == "*" or (
265
- obj.feature_type == self.prefix and obj.name == self.name
271
+ obj.feature_type == self.prefix and obj.name == self.name
266
272
  )
267
273
  return False
268
274
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "kodexa"
3
- version = "7.0.012396164812"
3
+ version = "7.0.012397101638"
4
4
  description = "Python SDK for the Kodexa Platform"
5
5
  authors = ["Austin Redenbaugh <austin@kodexa.com>", "Philip Dodds <philip@kodexa.com>", "Romar Cablao <rcablao@kodexa.com>", "Amadea Paula Dodds <amadeapaula@kodexa.com>"]
6
6
  readme = "README.md"