fhirpathpy 2.0.0__tar.gz → 2.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.
Files changed (54) hide show
  1. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/PKG-INFO +40 -8
  2. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/README.md +39 -8
  3. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/__init__.py +2 -4
  4. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/equality.py +2 -1
  5. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/nodes.py +2 -1
  6. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/util.py +1 -1
  7. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/ASTPathListener.py +17 -4
  8. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/pyproject.toml +16 -5
  9. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/LICENSE.md +0 -0
  10. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/__init__.py +0 -0
  11. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/evaluators/__init__.py +0 -0
  12. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/__init__.py +0 -0
  13. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/aggregate.py +0 -0
  14. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/collections.py +0 -0
  15. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/combining.py +0 -0
  16. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/constants.py +0 -0
  17. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/datetime.py +0 -0
  18. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/existence.py +0 -0
  19. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/filtering.py +0 -0
  20. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/logic.py +0 -0
  21. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/math.py +0 -0
  22. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/misc.py +0 -0
  23. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/navigation.py +0 -0
  24. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/strings.py +0 -0
  25. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/subsetting.py +0 -0
  26. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/engine/invocations/types.py +0 -0
  27. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/__init__.py +0 -0
  28. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/dstu2/choiceTypePaths.json +0 -0
  29. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/dstu2/path2Type.json +0 -0
  30. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/dstu2/pathsDefinedElsewhere.json +0 -0
  31. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/dstu2/type2Parent.json +0 -0
  32. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/r4/choiceTypePaths.json +0 -0
  33. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/r4/path2Type.json +0 -0
  34. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/r4/pathsDefinedElsewhere.json +0 -0
  35. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/r4/type2Parent.json +0 -0
  36. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/r5/choiceTypePaths.json +0 -0
  37. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/r5/path2Type.json +0 -0
  38. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/r5/pathsDefinedElsewhere.json +0 -0
  39. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/r5/type2Parent.json +0 -0
  40. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/stu3/choiceTypePaths.json +0 -0
  41. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/stu3/path2Type.json +0 -0
  42. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/stu3/pathsDefinedElsewhere.json +0 -0
  43. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/models/stu3/type2Parent.json +0 -0
  44. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/FHIRPath.g4 +0 -0
  45. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/README.md +0 -0
  46. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/__init__.py +0 -0
  47. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/generated/FHIRPath.interp +0 -0
  48. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/generated/FHIRPath.tokens +0 -0
  49. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/generated/FHIRPathLexer.interp +0 -0
  50. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/generated/FHIRPathLexer.py +0 -0
  51. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/generated/FHIRPathLexer.tokens +0 -0
  52. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/generated/FHIRPathListener.py +0 -0
  53. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/generated/FHIRPathParser.py +0 -0
  54. {fhirpathpy-2.0.0 → fhirpathpy-2.0.2}/fhirpathpy/parser/generated/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fhirpathpy
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: FHIRPath implementation in Python
5
5
  Keywords: fhir,fhirpath
6
6
  Author-email: "beda.software" <fhirpath@beda.software>
@@ -82,7 +82,7 @@ patient = {
82
82
  }
83
83
 
84
84
  # Evaluating FHIRPath
85
- result = evaluate(patient, "Patient.name.where(use='usual').given.first()", [])
85
+ result = evaluate(patient, "Patient.name.where(use='usual').given.first()", {})
86
86
  # result: `['Jim']`
87
87
  ```
88
88
 
@@ -97,11 +97,11 @@ path (string): fhirpath expression, sample 'Patient.name.given'
97
97
 
98
98
  context (dict): a hash of variable name/value pairs.
99
99
 
100
- model (dict): The "model" data object specific to a domain, e.g. R4.
100
+ model (dict): The "model" data object specific to a domain, e.g. R4. See Using data models documentation below.
101
101
 
102
- options (dict) - Custom options (see the documentation below)
102
+ options (dict) - Custom options.
103
103
 
104
- options.userInvocationTable - a user invocation table used to replace any existing functions or define new ones (see User-defined functions documentation below)
104
+ options.userInvocationTable - a user invocation table used to replace any existing functions or define new ones. See User-defined functions documentation below.
105
105
 
106
106
  ## compile
107
107
  Returns a function that takes a resource and an optional context hash (see "evaluate"), and returns the result of evaluating the given FHIRPath expression on that resource. The advantage of this function over "evaluate" is that if you have multiple resources, the given FHIRPath expression will only be parsed once
@@ -110,14 +110,38 @@ Returns a function that takes a resource and an optional context hash (see "eval
110
110
 
111
111
  path (string) - the FHIRPath expression to be parsed.
112
112
 
113
- model (dict) - The "model" data object specific to a domain, e.g. R4.
113
+ model (dict) - The "model" data object specific to a domain, e.g. R4. See Using data models documentation below.
114
114
 
115
115
  options (dict) - Custom options
116
116
 
117
- options.userInvocationTable - a user invocation table used to replace any existing functions or define new ones (see User-defined functions documentation below)
117
+ options.userInvocationTable - a user invocation table used to replace any existing functions or define new ones. See User-defined functions documentation below.
118
+
119
+ ## Using data models
120
+
121
+ The fhirpathpy library comes with pre-defined data models for FHIR versions DSTU2, STU3, R4, and R5. These models can be used within your project.
122
+
123
+ Example:
124
+ ```python
125
+ from fhirpathpy.models import models
126
+
127
+
128
+ r4_model = models["r4"]
129
+
130
+ patient = {
131
+ "resourceType": "Patient",
132
+ "deceasedBoolean": false,
133
+ }
134
+
135
+ result = evaluate(patient, "Patient.deceased", {}, r4_model)
136
+
137
+ # result: `[False]`
138
+ ```
118
139
 
119
140
  ## User-defined functions
120
141
 
142
+ The FHIRPath specification includes a set of built-in functions. However, if you need to extend the functionality by adding custom logic, you can define your own functions by providing a table of user-defined functions.
143
+
144
+ Example:
121
145
  ```python
122
146
  user_invocation_table = {
123
147
  "pow": {
@@ -132,7 +156,15 @@ result = evaluate(
132
156
  options={"userInvocationTable": user_invocation_table},
133
157
  )
134
158
 
135
- # result: [25, 36, 49]
159
+ # result: `[25, 36, 49]`
136
160
  ```
137
161
 
138
162
  It works similarly to [fhirpath.js](https://github.com/HL7/fhirpath.js/tree/master?tab=readme-ov-file#user-defined-functions)
163
+
164
+
165
+ ## Development
166
+
167
+ To activate git pre-commit hook: `autohooks activate`
168
+
169
+ To run tests: `pytest`
170
+
@@ -51,7 +51,7 @@ patient = {
51
51
  }
52
52
 
53
53
  # Evaluating FHIRPath
54
- result = evaluate(patient, "Patient.name.where(use='usual').given.first()", [])
54
+ result = evaluate(patient, "Patient.name.where(use='usual').given.first()", {})
55
55
  # result: `['Jim']`
56
56
  ```
57
57
 
@@ -66,11 +66,11 @@ path (string): fhirpath expression, sample 'Patient.name.given'
66
66
 
67
67
  context (dict): a hash of variable name/value pairs.
68
68
 
69
- model (dict): The "model" data object specific to a domain, e.g. R4.
69
+ model (dict): The "model" data object specific to a domain, e.g. R4. See Using data models documentation below.
70
70
 
71
- options (dict) - Custom options (see the documentation below)
71
+ options (dict) - Custom options.
72
72
 
73
- options.userInvocationTable - a user invocation table used to replace any existing functions or define new ones (see User-defined functions documentation below)
73
+ options.userInvocationTable - a user invocation table used to replace any existing functions or define new ones. See User-defined functions documentation below.
74
74
 
75
75
  ## compile
76
76
  Returns a function that takes a resource and an optional context hash (see "evaluate"), and returns the result of evaluating the given FHIRPath expression on that resource. The advantage of this function over "evaluate" is that if you have multiple resources, the given FHIRPath expression will only be parsed once
@@ -79,14 +79,38 @@ Returns a function that takes a resource and an optional context hash (see "eval
79
79
 
80
80
  path (string) - the FHIRPath expression to be parsed.
81
81
 
82
- model (dict) - The "model" data object specific to a domain, e.g. R4.
82
+ model (dict) - The "model" data object specific to a domain, e.g. R4. See Using data models documentation below.
83
83
 
84
84
  options (dict) - Custom options
85
85
 
86
- options.userInvocationTable - a user invocation table used to replace any existing functions or define new ones (see User-defined functions documentation below)
86
+ options.userInvocationTable - a user invocation table used to replace any existing functions or define new ones. See User-defined functions documentation below.
87
+
88
+ ## Using data models
89
+
90
+ The fhirpathpy library comes with pre-defined data models for FHIR versions DSTU2, STU3, R4, and R5. These models can be used within your project.
91
+
92
+ Example:
93
+ ```python
94
+ from fhirpathpy.models import models
95
+
96
+
97
+ r4_model = models["r4"]
98
+
99
+ patient = {
100
+ "resourceType": "Patient",
101
+ "deceasedBoolean": false,
102
+ }
103
+
104
+ result = evaluate(patient, "Patient.deceased", {}, r4_model)
105
+
106
+ # result: `[False]`
107
+ ```
87
108
 
88
109
  ## User-defined functions
89
110
 
111
+ The FHIRPath specification includes a set of built-in functions. However, if you need to extend the functionality by adding custom logic, you can define your own functions by providing a table of user-defined functions.
112
+
113
+ Example:
90
114
  ```python
91
115
  user_invocation_table = {
92
116
  "pow": {
@@ -101,7 +125,14 @@ result = evaluate(
101
125
  options={"userInvocationTable": user_invocation_table},
102
126
  )
103
127
 
104
- # result: [25, 36, 49]
128
+ # result: `[25, 36, 49]`
105
129
  ```
106
130
 
107
- It works similarly to [fhirpath.js](https://github.com/HL7/fhirpath.js/tree/master?tab=readme-ov-file#user-defined-functions)
131
+ It works similarly to [fhirpath.js](https://github.com/HL7/fhirpath.js/tree/master?tab=readme-ov-file#user-defined-functions)
132
+
133
+
134
+ ## Development
135
+
136
+ To activate git pre-commit hook: `autohooks activate`
137
+
138
+ To run tests: `pytest`
@@ -5,7 +5,7 @@ from fhirpathpy.engine.util import arraify, get_data, set_paths, process_user_in
5
5
  from fhirpathpy.engine.nodes import FP_Type, ResourceNode
6
6
 
7
7
  __title__ = "fhirpathpy"
8
- __version__ = "2.0.0"
8
+ __version__ = "2.0.2"
9
9
  __author__ = "beda.software"
10
10
  __license__ = "MIT"
11
11
  __copyright__ = "Copyright 2025 beda.software"
@@ -103,6 +103,4 @@ def compile(path, model=None, options=None):
103
103
 
104
104
  For example, you could pass in the result of require("fhirpath/fhir-context/r4")
105
105
  """
106
- return set_paths(
107
- apply_parsed_path, parsedPath=parse(path), model=model, options=options
108
- )
106
+ return set_paths(apply_parsed_path, parsedPath=parse(path), model=model, options=options)
@@ -82,6 +82,7 @@ def equivalence(ctx, x, y):
82
82
  return x_val.deep_equal(y_val)
83
83
 
84
84
  if isinstance(a, (abc.Mapping, list)) and isinstance(b, (abc.Mapping, list)):
85
+
85
86
  def deep_equal(a, b):
86
87
  if isinstance(a, abc.Mapping) and isinstance(b, abc.Mapping):
87
88
  if a.keys() != b.keys():
@@ -89,7 +90,7 @@ def equivalence(ctx, x, y):
89
90
  return all(deep_equal(a[key], b[key]) for key in a)
90
91
  elif isinstance(a, list) and isinstance(b, list):
91
92
  return len(a) == len(b) and all(
92
- deep_equal(x, y) for x, y in zip(sorted(a), sorted(b))
93
+ deep_equal(x, y) for x, y in zip(sorted(a), sorted(b), strict=True)
93
94
  )
94
95
  elif isinstance(a, str) and isinstance(b, str):
95
96
  return normalize_string(a) == normalize_string(b)
@@ -281,7 +281,8 @@ class FP_Quantity(FP_Type):
281
281
  return FP_Quantity(result, toUnit)
282
282
  return None
283
283
 
284
- def _compare_years_and_months(self, other, year_units=["year", "years"]):
284
+ def _compare_years_and_months(self, other, year_units=None):
285
+ year_units = year_units or ["year", "years"]
285
286
  self_value_in_months = self.value
286
287
  other_value_in_months = other.value
287
288
 
@@ -107,7 +107,7 @@ def process_user_invocation_table(table):
107
107
  return {
108
108
  name: {
109
109
  **entity,
110
- "fn": lambda ctx, inputs, *args: entity["fn"](
110
+ "fn": lambda ctx, inputs, *args, __fn__=entity["fn"]: __fn__(
111
111
  [get_data(i) for i in inputs], *args
112
112
  ),
113
113
  }
@@ -2,19 +2,32 @@ from antlr4.tree.Tree import TerminalNodeImpl
2
2
  from fhirpathpy.parser.generated.FHIRPathListener import FHIRPathListener
3
3
 
4
4
 
5
+ def has_node_type_text(node_type):
6
+ # In general we need mostly terminal nodes (e.g. Identifier and any Literal)
7
+ # But the code also uses TypeSpecifier, InvocationExpression and TermExpression
8
+ return node_type.endswith("Literal") or node_type in [
9
+ "LiteralTerm",
10
+ "Identifier",
11
+ "TypeSpecifier",
12
+ "InvocationExpression",
13
+ "TermExpression",
14
+ ]
15
+
16
+
5
17
  class ASTPathListener(FHIRPathListener):
6
18
  def __init__(self):
7
19
  self.parentStack = [{}]
8
20
 
9
21
  def pushNode(self, nodeType, ctx):
10
22
  parentNode = self.parentStack[-1]
11
- node = {"type": nodeType, "text": ctx.getText(), "terminalNodeText": []}
12
-
23
+ node = {"type": nodeType, "terminalNodeText": []}
24
+ if has_node_type_text(nodeType):
25
+ node["text"] = ctx.getText()
13
26
  for child in ctx.children:
14
27
  if isinstance(child, TerminalNodeImpl):
15
28
  node["terminalNodeText"].append(child.getText())
16
29
 
17
- if not "children" in parentNode:
30
+ if "children" not in parentNode:
18
31
  parentNode["children"] = []
19
32
 
20
33
  parentNode["children"].append(node)
@@ -28,7 +41,7 @@ class ASTPathListener(FHIRPathListener):
28
41
  def __getattribute__(self, name):
29
42
  attr = object.__getattribute__(self, name)
30
43
 
31
- if name in FHIRPathListener.__dict__ and hasattr(attr, "__call__"):
44
+ if name in FHIRPathListener.__dict__ and callable(attr):
32
45
 
33
46
  def newfunc(*args, **kwargs):
34
47
  if name.startswith("enter"):
@@ -12,11 +12,6 @@ exclude = '''
12
12
  )
13
13
  '''
14
14
 
15
- [tool.isort]
16
- profile = "black"
17
- line_length = 100
18
- skip = [".gitignore"]
19
-
20
15
  [tool.pytest.ini_options]
21
16
  minversion = "6.0"
22
17
  addopts = "-ra -q --color=yes --cov=fhirpathpy --cov-report=xml"
@@ -62,3 +57,19 @@ Homepage = "https://github.com/beda-software/fhirpath-py"
62
57
  Documentation = "https://github.com/beda-software/fhirpath-py#readme"
63
58
  Source = "https://github.com/beda-software/fhirpath-py.git"
64
59
  Changelog = "https://github.com/beda-software/fhirpath-py/blob/master/CHANGELOG.md"
60
+
61
+
62
+ [tool.ruff]
63
+ target-version = "py39"
64
+ line-length = 100
65
+ include = ["app/**/*.py", "tests/**/*.py"]
66
+
67
+ [tool.ruff.lint]
68
+ select = ["B", "F", "I", "E", "UP", "N", "PL", "PERF"]
69
+ # Black is responsible for E501
70
+ ignore = ["E501"]
71
+ unfixable = ["F401"]
72
+
73
+ [tool.autohooks]
74
+ mode = "pipenv"
75
+ pre-commit = ["autohooks.plugins.black", "autohooks.plugins.ruff"]
File without changes