fhirpathpy 2.2.0__tar.gz → 2.2.1__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.
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/PKG-INFO +2 -1
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/__init__.py +1 -1
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/__init__.py +12 -6
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/evaluators/__init__.py +23 -13
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/__init__.py +24 -21
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/collections.py +1 -1
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/combining.py +10 -1
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/equality.py +7 -8
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/existence.py +3 -6
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/filtering.py +7 -9
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/logic.py +10 -10
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/math.py +2 -2
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/misc.py +39 -36
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/navigation.py +11 -6
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/strings.py +3 -2
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/types.py +1 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/nodes.py +21 -18
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/util.py +6 -7
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/__init__.py +2 -3
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/pyproject.toml +4 -2
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/LICENSE.md +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/README.md +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/aggregate.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/constants.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/datetime.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/engine/invocations/subsetting.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/dstu2/choiceTypePaths.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/dstu2/path2Type.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/dstu2/pathsDefinedElsewhere.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/dstu2/type2Parent.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/r4/choiceTypePaths.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/r4/path2Type.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/r4/pathsDefinedElsewhere.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/r4/type2Parent.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/r5/choiceTypePaths.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/r5/path2Type.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/r5/pathsDefinedElsewhere.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/r5/type2Parent.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/stu3/choiceTypePaths.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/stu3/path2Type.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/stu3/pathsDefinedElsewhere.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/models/stu3/type2Parent.json +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/ASTPathListener.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/FHIRPath.g4 +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/README.md +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/__init__.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/generated/FHIRPath.interp +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/generated/FHIRPath.tokens +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/generated/FHIRPathLexer.interp +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/generated/FHIRPathLexer.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/generated/FHIRPathLexer.tokens +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/generated/FHIRPathListener.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/generated/FHIRPathParser.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/parser/generated/__init__.py +0 -0
- {fhirpathpy-2.2.0 → fhirpathpy-2.2.1}/fhirpathpy/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fhirpathpy
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: FHIRPath implementation in Python
|
|
5
5
|
Keywords: fhir,fhirpath
|
|
6
6
|
Author-email: "beda.software" <fhirpath@beda.software>
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
20
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
20
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
22
|
Classifier: Typing :: Typed
|
|
@@ -10,7 +10,7 @@ from fhirpathpy.engine.util import arraify, get_data, process_user_invocation_ta
|
|
|
10
10
|
from fhirpathpy.parser import parse
|
|
11
11
|
|
|
12
12
|
__title__ = "fhirpathpy"
|
|
13
|
-
__version__ = "2.2.
|
|
13
|
+
__version__ = "2.2.1"
|
|
14
14
|
__author__ = "beda.software"
|
|
15
15
|
__license__ = "MIT"
|
|
16
16
|
__copyright__ = "Copyright 2026 beda.software"
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import numbers
|
|
3
|
-
|
|
4
|
-
from fhirpathpy.engine
|
|
3
|
+
|
|
4
|
+
from fhirpathpy.engine import util
|
|
5
5
|
from fhirpathpy.engine.evaluators import evaluators
|
|
6
6
|
from fhirpathpy.engine.invocations import (
|
|
7
7
|
invocation_registry as base_invocation_registry,
|
|
8
8
|
)
|
|
9
|
+
from fhirpathpy.engine.nodes import TypeInfo
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def check_integer_param(val):
|
|
@@ -63,6 +64,14 @@ def doInvoke(ctx, fn_name, data, raw_params):
|
|
|
63
64
|
if "nullable_input" in invocation and util.is_nullable(data):
|
|
64
65
|
return []
|
|
65
66
|
|
|
67
|
+
if "variadic" in invocation:
|
|
68
|
+
tp = invocation["variadic"]
|
|
69
|
+
raw_params_list = raw_params if isinstance(raw_params, list) else []
|
|
70
|
+
thisValue = ctx["$this"] if "$this" in ctx else ctx["dataRoot"]
|
|
71
|
+
params = [make_param(ctx, thisValue, tp, pr) for pr in raw_params_list]
|
|
72
|
+
res = invocation["fn"](ctx, util.arraify(data), *params)
|
|
73
|
+
return util.arraify(res)
|
|
74
|
+
|
|
66
75
|
if "arity" not in invocation:
|
|
67
76
|
if raw_params is None or util.is_empty(raw_params):
|
|
68
77
|
res = invocation["fn"](ctx, util.arraify(data))
|
|
@@ -160,10 +169,7 @@ def make_param(ctx, parentData, node_type, param):
|
|
|
160
169
|
|
|
161
170
|
if len(res) > 1:
|
|
162
171
|
raise Exception(
|
|
163
|
-
"Unexpected collection"
|
|
164
|
-
+ json.dumps(res)
|
|
165
|
-
+ "; expected singleton of type "
|
|
166
|
-
+ node_type
|
|
172
|
+
"Unexpected collection" + json.dumps(res) + "; expected singleton of type " + node_type
|
|
167
173
|
)
|
|
168
174
|
|
|
169
175
|
if len(res) == 0:
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
1
3
|
from collections import abc
|
|
2
4
|
from decimal import Decimal
|
|
3
5
|
from functools import reduce
|
|
4
6
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import fhirpathpy.engine as engine
|
|
8
|
-
import fhirpathpy.engine.util as util
|
|
9
|
-
import fhirpathpy.engine.nodes as nodes
|
|
7
|
+
from fhirpathpy import engine
|
|
8
|
+
from fhirpathpy.engine import nodes, util
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
def boolean_literal(ctx, parentData, node):
|
|
@@ -71,7 +70,7 @@ def alias_op_expression(mapFn):
|
|
|
71
70
|
def func(ctx, parentData, node):
|
|
72
71
|
op = node["terminalNodeText"][0]
|
|
73
72
|
|
|
74
|
-
if not
|
|
73
|
+
if op not in mapFn:
|
|
75
74
|
raise Exception("Do not know how to alias " + op + " by " + json.dumps(mapFn))
|
|
76
75
|
|
|
77
76
|
alias = mapFn[op]
|
|
@@ -107,7 +106,7 @@ def external_constant_term(ctx, parent_data, node):
|
|
|
107
106
|
varName = identifier(ctx, parent_data, ext_identifier)[0].replace("`", "")
|
|
108
107
|
|
|
109
108
|
if varName not in ctx["vars"]:
|
|
110
|
-
raise ValueError(f
|
|
109
|
+
raise ValueError(f"Attempting to access an undefined environment variable: {varName}")
|
|
111
110
|
|
|
112
111
|
value = ctx["vars"][varName]
|
|
113
112
|
|
|
@@ -174,7 +173,9 @@ def create_reduce_member_invocation(model, key):
|
|
|
174
173
|
def func(acc, res):
|
|
175
174
|
res = nodes.ResourceNode.create_node(res)
|
|
176
175
|
childPath = f"{res.path}.{key}" if res.path else f"_.{key}"
|
|
177
|
-
fullPath =
|
|
176
|
+
fullPath = (
|
|
177
|
+
f"{res.propName}.{key}" if res.propName else childPath
|
|
178
|
+
) # The full path to the node (weill evenutally be) e.g. Patient.name[0].given
|
|
178
179
|
fullPath = fullPath.replace("_", "")
|
|
179
180
|
|
|
180
181
|
actualTypes = None
|
|
@@ -202,9 +203,8 @@ def create_reduce_member_invocation(model, key):
|
|
|
202
203
|
toAdd_ = res.data.get(f"_{key}")
|
|
203
204
|
if key == "extension":
|
|
204
205
|
childPath = "Extension"
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
toAdd = len(res.data)
|
|
206
|
+
elif key == "length":
|
|
207
|
+
toAdd = len(res.data)
|
|
208
208
|
|
|
209
209
|
childPath = (
|
|
210
210
|
model["path2Type"].get(childPath, childPath)
|
|
@@ -214,13 +214,23 @@ def create_reduce_member_invocation(model, key):
|
|
|
214
214
|
|
|
215
215
|
if util.is_some(toAdd):
|
|
216
216
|
if isinstance(toAdd, list):
|
|
217
|
-
mapped = [
|
|
217
|
+
mapped = [
|
|
218
|
+
nodes.ResourceNode.create_node(
|
|
219
|
+
x, childPath, propName=f"{fullPath}[{i}]", index=i
|
|
220
|
+
)
|
|
221
|
+
for i, x in enumerate(toAdd)
|
|
222
|
+
]
|
|
218
223
|
acc = acc + mapped
|
|
219
224
|
else:
|
|
220
225
|
acc.append(nodes.ResourceNode.create_node(toAdd, childPath, propName=fullPath))
|
|
221
226
|
if util.is_some(toAdd_):
|
|
222
227
|
if isinstance(toAdd_, list):
|
|
223
|
-
mapped = [
|
|
228
|
+
mapped = [
|
|
229
|
+
nodes.ResourceNode.create_node(
|
|
230
|
+
x, childPath, propName=f"{fullPath}[{i}]", index=i
|
|
231
|
+
)
|
|
232
|
+
for i, x in enumerate(toAdd_)
|
|
233
|
+
]
|
|
224
234
|
acc = acc + mapped
|
|
225
235
|
else:
|
|
226
236
|
acc.append(nodes.ResourceNode.create_node(toAdd_, childPath, propName=fullPath))
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
from fhirpathpy.engine.invocations import (
|
|
4
|
+
aggregate,
|
|
5
|
+
collections,
|
|
6
|
+
combining,
|
|
7
|
+
datetime,
|
|
8
|
+
equality,
|
|
9
|
+
existence,
|
|
10
|
+
filtering,
|
|
11
|
+
logic,
|
|
12
|
+
math,
|
|
13
|
+
misc,
|
|
14
|
+
navigation,
|
|
15
|
+
strings,
|
|
16
|
+
subsetting,
|
|
17
|
+
types,
|
|
18
|
+
)
|
|
17
19
|
from fhirpathpy.engine.nodes import FP_DateTime, FP_Quantity, FP_Time
|
|
18
20
|
|
|
19
21
|
invocation_registry = {
|
|
@@ -21,8 +23,8 @@ invocation_registry = {
|
|
|
21
23
|
"not": {"fn": existence.not_fn},
|
|
22
24
|
"exists": {"fn": existence.exists_macro, "arity": {0: [], 1: ["Expr"]}},
|
|
23
25
|
"all": {"fn": existence.all_macro, "arity": {1: ["Expr"]}},
|
|
24
|
-
"union": {"fn": combining.union_op,
|
|
25
|
-
"exclude": {"fn": combining.exclude_fn,
|
|
26
|
+
"union": {"fn": combining.union_op, "arity": {1: ["AnyAtRoot"]}},
|
|
27
|
+
"exclude": {"fn": combining.exclude_fn, "arity": {1: ["AnyAtRoot"]}},
|
|
26
28
|
"allTrue": {"fn": existence.all_true_fn},
|
|
27
29
|
"anyTrue": {"fn": existence.any_true_fn},
|
|
28
30
|
"allFalse": {"fn": existence.all_false_fn},
|
|
@@ -48,6 +50,7 @@ invocation_registry = {
|
|
|
48
50
|
"skip": {"fn": filtering.skip_fn, "arity": {1: ["Integer"]}},
|
|
49
51
|
"intersect": {"fn": subsetting.intersect_fn, "arity": {1: ["AnyAtRoot"]}},
|
|
50
52
|
"combine": {"fn": combining.combine_fn, "arity": {1: ["AnyAtRoot"]}},
|
|
53
|
+
"coalesce": {"fn": combining.coalesce_fn, "variadic": "Expr"},
|
|
51
54
|
"iif": {"fn": misc.iif_macro, "arity": {2: ["Expr", "Expr"], 3: ["Expr", "Expr", "Expr"]}},
|
|
52
55
|
"trace": {"fn": misc.trace_fn, "arity": {0: [], 1: ["String"]}},
|
|
53
56
|
"toInteger": {"fn": misc.to_integer},
|
|
@@ -109,8 +112,8 @@ invocation_registry = {
|
|
|
109
112
|
">=": {"fn": equality.gte, "arity": {2: ["Any", "Any"]}, "nullable": True},
|
|
110
113
|
"containsOp": {"fn": collections.contains, "arity": {2: ["Any", "Any"]}},
|
|
111
114
|
"inOp": {"fn": collections.inn, "arity": {2: ["Any", "Any"]}},
|
|
112
|
-
"isOp": {"fn": types.is_fn,
|
|
113
|
-
"asOp": {"fn": types.as_fn,
|
|
115
|
+
"isOp": {"fn": types.is_fn, "arity": {2: ["Any", "TypeSpecifier"]}},
|
|
116
|
+
"asOp": {"fn": types.as_fn, "arity": {2: ["Any", "TypeSpecifier"]}},
|
|
114
117
|
"&": {"fn": math.amp, "arity": {2: ["String", "String"]}},
|
|
115
118
|
"+": {"fn": math.plus, "arity": {2: ["Any", "Any"]}, "nullable": True},
|
|
116
119
|
"-": {"fn": math.minus, "arity": {2: ["Any", "Any"]}, "nullable": True},
|
|
@@ -127,10 +130,10 @@ invocation_registry = {
|
|
|
127
130
|
"min": {"fn": aggregate.min_fn},
|
|
128
131
|
"max": {"fn": aggregate.max_fn},
|
|
129
132
|
"aggregate": {"fn": aggregate.aggregate_macro, "arity": {1: ["Expr"], 2: ["Expr", "Any"]}},
|
|
130
|
-
"convertsToBoolean": {"fn": misc.create_converts_to_fn(misc.to_boolean,
|
|
131
|
-
"convertsToInteger": {"fn": misc.create_converts_to_fn(misc.to_integer,
|
|
133
|
+
"convertsToBoolean": {"fn": misc.create_converts_to_fn(misc.to_boolean, "bool")},
|
|
134
|
+
"convertsToInteger": {"fn": misc.create_converts_to_fn(misc.to_integer, "int")},
|
|
132
135
|
"convertsToDecimal": {"fn": misc.create_converts_to_fn(misc.to_decimal, Decimal)},
|
|
133
|
-
"convertsToString": {"fn": misc.create_converts_to_fn(misc.to_string,
|
|
136
|
+
"convertsToString": {"fn": misc.create_converts_to_fn(misc.to_string, "str")},
|
|
134
137
|
"convertsToDate": {"fn": misc.create_converts_to_fn(misc.to_date, FP_DateTime)},
|
|
135
138
|
"convertsToDateTime": {"fn": misc.create_converts_to_fn(misc.to_date_time, FP_DateTime)},
|
|
136
139
|
"convertsToTime": {"fn": misc.create_converts_to_fn(misc.to_time, FP_Time)},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
from fhirpathpy.engine import util
|
|
2
|
+
from fhirpathpy.engine.invocations import existence
|
|
2
3
|
|
|
3
4
|
"""
|
|
4
5
|
This file holds code to hande the FHIRPath Combining functions
|
|
@@ -15,3 +16,11 @@ def combine_fn(ctx, coll1, coll2):
|
|
|
15
16
|
|
|
16
17
|
def exclude_fn(ctx, coll1, coll2):
|
|
17
18
|
return [element for element in coll1 if element not in coll2]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def coalesce_fn(ctx, data, *exprs):
|
|
22
|
+
for expr in exprs:
|
|
23
|
+
result = expr(data)
|
|
24
|
+
if not util.is_empty(result):
|
|
25
|
+
return result
|
|
26
|
+
return []
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import json
|
|
1
2
|
from collections import abc
|
|
2
3
|
from decimal import Decimal
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import fhirpathpy.engine.nodes as nodes
|
|
4
|
+
|
|
5
|
+
from fhirpathpy.engine import nodes, util
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
8
|
This file holds code to hande the FHIRPath Math functions.
|
|
@@ -81,7 +81,7 @@ def equivalence(ctx, x, y):
|
|
|
81
81
|
if isinstance(x_val, nodes.FP_Quantity) and isinstance(y_val, nodes.FP_Quantity):
|
|
82
82
|
return x_val.deep_equal(y_val)
|
|
83
83
|
|
|
84
|
-
if isinstance(a,
|
|
84
|
+
if isinstance(a, abc.Mapping | list) and isinstance(b, abc.Mapping | list):
|
|
85
85
|
|
|
86
86
|
def deep_equal(a, b):
|
|
87
87
|
if isinstance(a, abc.Mapping) and isinstance(b, abc.Mapping):
|
|
@@ -94,7 +94,7 @@ def equivalence(ctx, x, y):
|
|
|
94
94
|
)
|
|
95
95
|
elif isinstance(a, str) and isinstance(b, str):
|
|
96
96
|
return normalize_string(a) == normalize_string(b)
|
|
97
|
-
elif isinstance(a,
|
|
97
|
+
elif isinstance(a, int | float) and isinstance(b, int | float):
|
|
98
98
|
return abs(a - b) < 0.5
|
|
99
99
|
else:
|
|
100
100
|
return a == b
|
|
@@ -190,12 +190,11 @@ def typecheck(a, b):
|
|
|
190
190
|
if lClass != rClass and not areNumbers:
|
|
191
191
|
d = None
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
if lClass == str and (rClass == nodes.FP_DateTime or rClass == nodes.FP_Time):
|
|
193
|
+
if lClass is str and (rClass in (nodes.FP_DateTime, nodes.FP_Time)):
|
|
195
194
|
d = nodes.FP_DateTime(a) or nodes.FP_Time(a)
|
|
196
195
|
if d is not None:
|
|
197
196
|
rtn = [d, b]
|
|
198
|
-
elif rClass
|
|
197
|
+
elif rClass is str and (lClass in (nodes.FP_DateTime, nodes.FP_Time)):
|
|
199
198
|
d = nodes.FP_DateTime(b) or nodes.FP_Time(b)
|
|
200
199
|
if d is not None:
|
|
201
200
|
rtn = [a, d]
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
|
-
from fhirpathpy.engine.invocations import misc
|
|
3
|
-
from fhirpathpy.engine.invocations.misc import to_boolean
|
|
4
|
-
import fhirpathpy.engine.util as util
|
|
5
|
-
import fhirpathpy.engine.nodes as nodes
|
|
6
|
-
import fhirpathpy.engine.invocations.filtering as filtering
|
|
7
2
|
|
|
3
|
+
from fhirpathpy.engine import nodes, util
|
|
4
|
+
from fhirpathpy.engine.invocations import filtering, misc
|
|
8
5
|
|
|
9
6
|
"""
|
|
10
7
|
This file holds code to hande the FHIRPath Existence functions
|
|
@@ -54,7 +51,7 @@ def all_macro(ctx, colls, expr):
|
|
|
54
51
|
|
|
55
52
|
def extract_boolean_value(data):
|
|
56
53
|
value = util.get_data(data)
|
|
57
|
-
if type(value)
|
|
54
|
+
if type(value) is not bool:
|
|
58
55
|
raise Exception("Found type '" + type(data) + "' but was expecting bool")
|
|
59
56
|
return value
|
|
60
57
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from collections import abc
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import fhirpathpy.engine.util as util
|
|
5
|
-
import fhirpathpy.engine.nodes as nodes
|
|
2
|
+
|
|
3
|
+
from fhirpathpy.engine import nodes, util
|
|
6
4
|
|
|
7
5
|
# Contains the FHIRPath Filtering and Projection functions.
|
|
8
6
|
# (Section 5.2 of the FHIRPath 1.0.0 specification).
|
|
@@ -54,18 +52,18 @@ def repeat_macro(ctx, data, expr):
|
|
|
54
52
|
res = []
|
|
55
53
|
items = data
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
next_item = None
|
|
58
56
|
lres = None
|
|
59
57
|
|
|
60
58
|
uniq = set()
|
|
61
59
|
|
|
62
60
|
while len(items) != 0:
|
|
63
|
-
|
|
61
|
+
next_item = items[0]
|
|
64
62
|
items = items[1:]
|
|
65
|
-
lres = [
|
|
63
|
+
lres = [elem for elem in expr(next_item) if elem not in uniq]
|
|
66
64
|
if len(lres) > 0:
|
|
67
|
-
for
|
|
68
|
-
uniq.add(
|
|
65
|
+
for elem in lres:
|
|
66
|
+
uniq.add(elem)
|
|
69
67
|
res = res + lres
|
|
70
68
|
items = items + lres
|
|
71
69
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
def or_op(ctx, a, b):
|
|
2
2
|
if isinstance(b, list):
|
|
3
|
-
if a
|
|
3
|
+
if a is True:
|
|
4
4
|
return True
|
|
5
|
-
if a
|
|
5
|
+
if a is False:
|
|
6
6
|
return []
|
|
7
7
|
if isinstance(a, list):
|
|
8
8
|
return []
|
|
9
9
|
if isinstance(a, list):
|
|
10
|
-
if b
|
|
10
|
+
if b is True:
|
|
11
11
|
return True
|
|
12
12
|
return []
|
|
13
13
|
|
|
@@ -16,15 +16,15 @@ def or_op(ctx, a, b):
|
|
|
16
16
|
|
|
17
17
|
def and_op(ctx, a, b):
|
|
18
18
|
if isinstance(b, list):
|
|
19
|
-
if a
|
|
19
|
+
if a is True:
|
|
20
20
|
return []
|
|
21
|
-
if a
|
|
21
|
+
if a is False:
|
|
22
22
|
return False
|
|
23
23
|
if isinstance(a, list):
|
|
24
24
|
return []
|
|
25
25
|
|
|
26
26
|
if isinstance(a, list):
|
|
27
|
-
if b
|
|
27
|
+
if b is True:
|
|
28
28
|
return []
|
|
29
29
|
return False
|
|
30
30
|
|
|
@@ -42,19 +42,19 @@ def xor_op(ctx, a, b):
|
|
|
42
42
|
|
|
43
43
|
def implies_op(ctx, a, b):
|
|
44
44
|
if isinstance(b, list):
|
|
45
|
-
if a
|
|
45
|
+
if a is True:
|
|
46
46
|
return []
|
|
47
|
-
if a
|
|
47
|
+
if a is False:
|
|
48
48
|
return True
|
|
49
49
|
if isinstance(a, list):
|
|
50
50
|
return []
|
|
51
51
|
|
|
52
52
|
if isinstance(a, list):
|
|
53
|
-
if b
|
|
53
|
+
if b is True:
|
|
54
54
|
return True
|
|
55
55
|
return []
|
|
56
56
|
|
|
57
|
-
if a
|
|
57
|
+
if a is False:
|
|
58
58
|
return True
|
|
59
59
|
|
|
60
60
|
return a and b
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from fhirpathpy.engine import nodes, util
|
|
2
4
|
from fhirpathpy.engine.invocations.equality import remove_duplicate_extension
|
|
3
|
-
import fhirpathpy.engine.util as util
|
|
4
|
-
import fhirpathpy.engine.nodes as nodes
|
|
5
5
|
|
|
6
6
|
"""
|
|
7
7
|
Adds the math functions to the given FHIRPath engine.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from decimal import Decimal
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
import fhirpathpy.engine.nodes as nodes
|
|
4
|
+
from fhirpathpy.engine import nodes, util
|
|
6
5
|
|
|
7
6
|
# This file holds code to hande the FHIRPath Existence functions (5.1 in the
|
|
8
7
|
# specification).
|
|
@@ -36,10 +35,10 @@ def to_integer(ctx, coll):
|
|
|
36
35
|
|
|
37
36
|
value = util.get_data(coll[0])
|
|
38
37
|
|
|
39
|
-
if value
|
|
38
|
+
if value is False:
|
|
40
39
|
return 0
|
|
41
40
|
|
|
42
|
-
if value
|
|
41
|
+
if value is True:
|
|
43
42
|
return 1
|
|
44
43
|
|
|
45
44
|
if util.is_number(value):
|
|
@@ -72,7 +71,7 @@ def to_quantity(ctx, coll, to_unit=None):
|
|
|
72
71
|
v = util.val_data_converted(coll[0])
|
|
73
72
|
quantity_regex_res = None
|
|
74
73
|
|
|
75
|
-
if isinstance(v,
|
|
74
|
+
if isinstance(v, int | Decimal):
|
|
76
75
|
result = nodes.FP_Quantity(v, "'1'")
|
|
77
76
|
elif isinstance(v, nodes.FP_Quantity):
|
|
78
77
|
result = v
|
|
@@ -130,62 +129,63 @@ def to_string(ctx, coll):
|
|
|
130
129
|
|
|
131
130
|
def to_date_time(ctx, coll):
|
|
132
131
|
ln = len(coll)
|
|
133
|
-
rtn = []
|
|
134
132
|
if ln > 1:
|
|
135
133
|
raise Exception("to_date_time called for a collection of length " + str(ln))
|
|
136
134
|
|
|
137
|
-
if ln
|
|
138
|
-
|
|
135
|
+
if ln != 1:
|
|
136
|
+
return []
|
|
139
137
|
|
|
140
|
-
|
|
138
|
+
value = util.get_data(coll[0])
|
|
139
|
+
dateTimeObject = nodes.FP_DateTime(value)
|
|
141
140
|
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
if not dateTimeObject:
|
|
142
|
+
return []
|
|
144
143
|
|
|
145
|
-
return util.get_data(
|
|
144
|
+
return util.get_data(dateTimeObject)
|
|
146
145
|
|
|
147
146
|
|
|
148
147
|
def to_time(ctx, coll):
|
|
149
148
|
ln = len(coll)
|
|
150
|
-
rtn = []
|
|
151
149
|
if ln > 1:
|
|
152
150
|
raise Exception("to_time called for a collection of length " + str(ln))
|
|
153
151
|
|
|
154
|
-
if ln
|
|
155
|
-
|
|
152
|
+
if ln != 1:
|
|
153
|
+
return []
|
|
156
154
|
|
|
157
|
-
|
|
155
|
+
value = util.get_data(coll[0])
|
|
156
|
+
timeObject = nodes.FP_Time(value)
|
|
158
157
|
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
if not timeObject:
|
|
159
|
+
return []
|
|
161
160
|
|
|
162
|
-
return util.get_data(
|
|
161
|
+
return util.get_data(timeObject)
|
|
163
162
|
|
|
164
163
|
|
|
165
164
|
def to_date(ctx, coll):
|
|
166
165
|
ln = len(coll)
|
|
167
|
-
rtn = []
|
|
168
|
-
|
|
169
166
|
if ln > 1:
|
|
170
167
|
raise Exception("to_date called for a collection of length " + str(ln))
|
|
171
168
|
|
|
172
|
-
if ln
|
|
173
|
-
|
|
169
|
+
if ln != 1:
|
|
170
|
+
return []
|
|
174
171
|
|
|
175
|
-
|
|
172
|
+
value = util.get_data(coll[0])
|
|
173
|
+
dateObject = nodes.FP_DateTime(value)
|
|
176
174
|
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
if not dateObject:
|
|
176
|
+
return []
|
|
179
177
|
|
|
180
|
-
return util.get_data(
|
|
178
|
+
return util.get_data(dateObject)
|
|
181
179
|
|
|
182
180
|
|
|
183
181
|
def create_converts_to_fn(to_function, _type):
|
|
184
182
|
if isinstance(_type, str):
|
|
183
|
+
|
|
185
184
|
def in_function(ctx, coll):
|
|
186
185
|
if len(coll) != 1:
|
|
187
186
|
return []
|
|
188
187
|
return type(to_function(ctx, coll)).__name__ == _type
|
|
188
|
+
|
|
189
189
|
return in_function
|
|
190
190
|
|
|
191
191
|
def in_function(ctx, coll):
|
|
@@ -198,8 +198,8 @@ def create_converts_to_fn(to_function, _type):
|
|
|
198
198
|
|
|
199
199
|
|
|
200
200
|
def to_boolean(ctx, coll):
|
|
201
|
-
true_strings = [
|
|
202
|
-
false_strings = [
|
|
201
|
+
true_strings = ["true", "t", "yes", "y", "1", "1.0"]
|
|
202
|
+
false_strings = ["false", "f", "no", "n", "0", "0.0"]
|
|
203
203
|
|
|
204
204
|
if len(coll) != 1:
|
|
205
205
|
return []
|
|
@@ -209,10 +209,10 @@ def to_boolean(ctx, coll):
|
|
|
209
209
|
|
|
210
210
|
if var_type == "bool":
|
|
211
211
|
return val
|
|
212
|
-
elif var_type
|
|
213
|
-
if val
|
|
212
|
+
elif var_type in ("int", "float"):
|
|
213
|
+
if val in (1, 1.0):
|
|
214
214
|
return True
|
|
215
|
-
elif val
|
|
215
|
+
elif val in (0, 0.0):
|
|
216
216
|
return False
|
|
217
217
|
elif var_type == "str":
|
|
218
218
|
lower_case_var = val.lower()
|
|
@@ -231,25 +231,28 @@ def boolean_singleton(coll):
|
|
|
231
231
|
elif len(coll) == 1:
|
|
232
232
|
return True
|
|
233
233
|
|
|
234
|
+
|
|
234
235
|
def string_singleton(coll):
|
|
235
236
|
d = util.get_data(coll[0])
|
|
236
237
|
if isinstance(d, str):
|
|
237
238
|
return d
|
|
238
239
|
|
|
240
|
+
|
|
239
241
|
singleton_eval_by_type = {
|
|
240
242
|
"Boolean": boolean_singleton,
|
|
241
243
|
"String": string_singleton,
|
|
242
244
|
}
|
|
243
245
|
|
|
246
|
+
|
|
244
247
|
def singleton(coll, type):
|
|
245
248
|
if len(coll) > 1:
|
|
246
|
-
raise Exception("Unexpected collection {coll}; expected singleton of type {type}"
|
|
249
|
+
raise Exception(f"Unexpected collection {coll}; expected singleton of type {type}")
|
|
247
250
|
elif len(coll) == 0:
|
|
248
251
|
return []
|
|
249
252
|
to_singleton = singleton_eval_by_type[type]
|
|
250
253
|
if to_singleton:
|
|
251
254
|
val = to_singleton(coll)
|
|
252
|
-
if
|
|
255
|
+
if val is not None:
|
|
253
256
|
return val
|
|
254
|
-
raise Exception("Expected {type}, but got: {coll}"
|
|
255
|
-
raise Exception("Not supported type {}"
|
|
257
|
+
raise Exception(f"Expected {type.lower()}, but got: {coll}")
|
|
258
|
+
raise Exception(f"Not supported type {type}")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from collections import abc
|
|
2
2
|
from functools import reduce
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
|
|
4
|
+
from fhirpathpy.engine import nodes, util
|
|
5
5
|
|
|
6
6
|
create_node = nodes.ResourceNode.create_node
|
|
7
7
|
|
|
@@ -30,7 +30,9 @@ def create_reduce_children(ctx, exclude_primitive_extensions):
|
|
|
30
30
|
if res.path is not None:
|
|
31
31
|
childPath = res.path + "." + prop
|
|
32
32
|
|
|
33
|
-
fullPath =
|
|
33
|
+
fullPath = (
|
|
34
|
+
f"{res.propName}.{prop}" if res.propName else childPath
|
|
35
|
+
) # The full path to the node (weill evenutally be) e.g. Patient.name[0].given
|
|
34
36
|
fullPath = fullPath.replace("_", "")
|
|
35
37
|
|
|
36
38
|
if prop == "extension":
|
|
@@ -52,14 +54,17 @@ def create_reduce_children(ctx, exclude_primitive_extensions):
|
|
|
52
54
|
# If the prop tolower ends with the type tolower
|
|
53
55
|
if prop.lower().endswith(childPath.lower()) and len(prop) > len(childPath):
|
|
54
56
|
# Check if the path is actually in the choice types
|
|
55
|
-
altPropName = res.path + "." + prop[
|
|
57
|
+
altPropName = res.path + "." + prop[: -len(childPath)]
|
|
56
58
|
actualTypes = model["choiceTypePaths"].get(altPropName, [])
|
|
57
59
|
if len(actualTypes) > 0:
|
|
58
60
|
# If it is, we can use it
|
|
59
|
-
fullPath = f"{res.propName}.{prop[
|
|
61
|
+
fullPath = f"{res.propName}.{prop[: -len(childPath)]}"
|
|
60
62
|
|
|
61
63
|
if isinstance(value, list):
|
|
62
|
-
mapped = [
|
|
64
|
+
mapped = [
|
|
65
|
+
create_node(n, childPath, propName=f"{fullPath}[{i}]", index=i)
|
|
66
|
+
for i, n in enumerate(value)
|
|
67
|
+
]
|
|
63
68
|
acc = acc + mapped
|
|
64
69
|
else:
|
|
65
70
|
acc.append(create_node(value, childPath, propName=fullPath))
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import re
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
from fhirpathpy.engine import util
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
def ensure_string_singleton(x):
|
|
7
8
|
if len(x) == 1:
|
|
8
9
|
d = util.get_data(x[0])
|
|
9
|
-
if
|
|
10
|
+
if isinstance(d, str):
|
|
10
11
|
return d
|
|
11
12
|
raise Exception("Expected string, but got " + str(d))
|
|
12
13
|
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
from collections import abc
|
|
2
1
|
import copy
|
|
3
|
-
from datetime import datetime, timedelta, timezone
|
|
4
|
-
from dateutil.relativedelta import relativedelta
|
|
5
|
-
from dateutil import parser, tz
|
|
6
|
-
from decimal import ROUND_HALF_UP, ROUND_UP, Decimal
|
|
7
|
-
import math
|
|
8
2
|
import json
|
|
3
|
+
import math
|
|
9
4
|
import re
|
|
10
|
-
import
|
|
11
|
-
from
|
|
5
|
+
from collections import abc
|
|
6
|
+
from datetime import datetime, timedelta, timezone
|
|
7
|
+
from decimal import ROUND_HALF_UP, ROUND_UP, Decimal
|
|
12
8
|
|
|
9
|
+
from dateutil import parser, tz
|
|
10
|
+
from dateutil.relativedelta import relativedelta
|
|
13
11
|
|
|
14
12
|
timeRE = (
|
|
15
13
|
r"^T?([0-9]{2})(?::([0-9]{2}))?(?::([0-9]{2}))?(?:\.([0-9]+))?(Z|(\+|-)[0-9]{2}(:[0-9]{2})?)?$"
|
|
@@ -258,8 +256,9 @@ class FP_Quantity(FP_Type):
|
|
|
258
256
|
fromUnit in FP_Quantity._m_cm_mm_conversion_factor
|
|
259
257
|
or toUnit in FP_Quantity._m_cm_mm_conversion_factor
|
|
260
258
|
):
|
|
261
|
-
from_magnitude, to_magnitude =
|
|
262
|
-
|
|
259
|
+
from_magnitude, to_magnitude = (
|
|
260
|
+
Decimal(from_m_cm_mm_magnitude),
|
|
261
|
+
Decimal(to_m_cm_mm_magnitude),
|
|
263
262
|
)
|
|
264
263
|
return FP_Quantity(from_magnitude * value / to_magnitude, toUnit)
|
|
265
264
|
|
|
@@ -343,7 +342,7 @@ class FP_TimeBase(FP_Type):
|
|
|
343
342
|
2012-01 = 2011 returns false
|
|
344
343
|
2012-01 ~ 2012 returns false
|
|
345
344
|
"""
|
|
346
|
-
if type(otherDateTime)
|
|
345
|
+
if type(otherDateTime) is not type(self):
|
|
347
346
|
return False
|
|
348
347
|
|
|
349
348
|
thisdt_list = self._getMatchAsList()
|
|
@@ -355,7 +354,7 @@ class FP_TimeBase(FP_Type):
|
|
|
355
354
|
indices_to_remove = [
|
|
356
355
|
i
|
|
357
356
|
for i in range(len(normalized_thisdt_list))
|
|
358
|
-
if normalized_thisdt_list[i]
|
|
357
|
+
if normalized_thisdt_list[i] is None and normalized_otherdt_list[i] is None
|
|
359
358
|
]
|
|
360
359
|
|
|
361
360
|
for i in reversed(indices_to_remove):
|
|
@@ -413,7 +412,7 @@ class FP_TimeBase(FP_Type):
|
|
|
413
412
|
]
|
|
414
413
|
|
|
415
414
|
def compare(self, otherDateTime):
|
|
416
|
-
if type(otherDateTime)
|
|
415
|
+
if type(otherDateTime) is not type(self):
|
|
417
416
|
raise TypeError
|
|
418
417
|
|
|
419
418
|
thisDateTimeList = self._getMatchAsList()
|
|
@@ -424,7 +423,7 @@ class FP_TimeBase(FP_Type):
|
|
|
424
423
|
indices_to_remove = [
|
|
425
424
|
i
|
|
426
425
|
for i in range(len(normalized_thisdt_list))
|
|
427
|
-
if normalized_thisdt_list[i]
|
|
426
|
+
if normalized_thisdt_list[i] is None and normalized_otherdt_list[i] is None
|
|
428
427
|
]
|
|
429
428
|
for i in reversed(indices_to_remove):
|
|
430
429
|
del normalized_thisdt_list[i]
|
|
@@ -598,7 +597,7 @@ class FP_Time(FP_TimeBase):
|
|
|
598
597
|
if not re.match(timeRE, dateStr):
|
|
599
598
|
return None
|
|
600
599
|
|
|
601
|
-
return super(
|
|
600
|
+
return super().__new__(cls)
|
|
602
601
|
|
|
603
602
|
def __init__(self, timeStr):
|
|
604
603
|
self.asStr = timeStr if isinstance(timeStr, str) else None
|
|
@@ -647,6 +646,8 @@ class FP_Time(FP_TimeBase):
|
|
|
647
646
|
return time_str
|
|
648
647
|
return self.asStr
|
|
649
648
|
|
|
649
|
+
__hash__ = None
|
|
650
|
+
|
|
650
651
|
def __eq__(self, other):
|
|
651
652
|
if isinstance(other, str):
|
|
652
653
|
return self.getTimeMatchStr()
|
|
@@ -705,7 +706,7 @@ class FP_DateTime(FP_TimeBase):
|
|
|
705
706
|
if not re.match(dateTimeRE, dateStr):
|
|
706
707
|
return None
|
|
707
708
|
|
|
708
|
-
return super(
|
|
709
|
+
return super().__new__(cls)
|
|
709
710
|
|
|
710
711
|
def __init__(self, dateStr):
|
|
711
712
|
self.asStr = dateStr if isinstance(dateStr, str) else None
|
|
@@ -735,6 +736,8 @@ class FP_DateTime(FP_TimeBase):
|
|
|
735
736
|
return iso_str
|
|
736
737
|
return self.asStr
|
|
737
738
|
|
|
739
|
+
__hash__ = None
|
|
740
|
+
|
|
738
741
|
def __eq__(self, other):
|
|
739
742
|
if isinstance(other, str):
|
|
740
743
|
return self.getDateTimeMatchStr()
|
|
@@ -833,8 +836,8 @@ class ResourceNode:
|
|
|
833
836
|
self.path = path
|
|
834
837
|
self.data = data
|
|
835
838
|
self._data = _data
|
|
836
|
-
self.propName:
|
|
837
|
-
self.index:
|
|
839
|
+
self.propName: str | None = propName
|
|
840
|
+
self.index: int | None = index
|
|
838
841
|
|
|
839
842
|
def __eq__(self, value):
|
|
840
843
|
if isinstance(value, ResourceNode):
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from decimal import Decimal
|
|
2
1
|
import json
|
|
3
2
|
from collections import OrderedDict
|
|
3
|
+
from decimal import Decimal
|
|
4
4
|
from functools import reduce
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
from fhirpathpy.engine.nodes import FP_Quantity, ResourceNode
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class set_paths:
|
|
@@ -13,9 +14,7 @@ class set_paths:
|
|
|
13
14
|
self.options = options
|
|
14
15
|
|
|
15
16
|
def __call__(self, resource, context=None):
|
|
16
|
-
return self.func(
|
|
17
|
-
resource, self.parsedPath, context or {}, self.model, self.options
|
|
18
|
-
)
|
|
17
|
+
return self.func(resource, self.parsedPath, context or {}, self.model, self.options)
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
def get_data(value):
|
|
@@ -41,7 +40,7 @@ def parse_value(value):
|
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
def is_number(value):
|
|
44
|
-
return isinstance(value,
|
|
43
|
+
return isinstance(value, int | Decimal | complex) and not isinstance(value, bool)
|
|
45
44
|
|
|
46
45
|
|
|
47
46
|
def is_capitalized(x):
|
|
@@ -61,7 +60,7 @@ def is_nullable(x):
|
|
|
61
60
|
|
|
62
61
|
|
|
63
62
|
def is_true(x):
|
|
64
|
-
return x
|
|
63
|
+
return x is True or isinstance(x, list) and len(x) == 1 and x[0] is True
|
|
65
64
|
|
|
66
65
|
|
|
67
66
|
def arraify(x, instead_none=None):
|
|
@@ -30,6 +30,7 @@ classifiers = [
|
|
|
30
30
|
"Programming Language :: Python :: 3.11",
|
|
31
31
|
"Programming Language :: Python :: 3.12",
|
|
32
32
|
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
33
34
|
"Topic :: Internet :: WWW/HTTP",
|
|
34
35
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
36
|
"Typing :: Typed",
|
|
@@ -50,11 +51,12 @@ Changelog = "https://github.com/beda-software/fhirpath-py/blob/master/CHANGELOG.
|
|
|
50
51
|
target-version = "py310"
|
|
51
52
|
line-length = 100
|
|
52
53
|
include = ["fhirpathpy/**/*.py", "tests/**/*.py"]
|
|
54
|
+
exclude = ["fhirpathpy/parser/**/*.py"]
|
|
53
55
|
|
|
54
56
|
[tool.ruff.lint]
|
|
55
57
|
select = ["B", "F", "I", "E", "UP", "N", "PL", "PERF"]
|
|
56
|
-
#
|
|
57
|
-
ignore = ["E501", "N803", "N806"]
|
|
58
|
+
# N8* is not relevant for us because of legacy code
|
|
59
|
+
ignore = ["E501", "N801", "N802", "N805", "N815", "N816", "N803", "N806", "PLR0911", "PLR0912", "PLR0915", "PLR2004", "PLW0211"]
|
|
58
60
|
unfixable = ["F401"]
|
|
59
61
|
|
|
60
62
|
[tool.mypy]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|