fhirpathpy 2.1.0__tar.gz → 2.2.0__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 (55) hide show
  1. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/PKG-INFO +7 -5
  2. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/README.md +1 -1
  3. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/__init__.py +109 -7
  4. fhirpathpy-2.2.0/fhirpathpy/py.typed +0 -0
  5. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/pyproject.toml +14 -22
  6. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/LICENSE.md +0 -0
  7. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/__init__.py +0 -0
  8. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/evaluators/__init__.py +0 -0
  9. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/__init__.py +0 -0
  10. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/aggregate.py +0 -0
  11. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/collections.py +0 -0
  12. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/combining.py +0 -0
  13. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/constants.py +0 -0
  14. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/datetime.py +0 -0
  15. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/equality.py +0 -0
  16. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/existence.py +0 -0
  17. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/filtering.py +0 -0
  18. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/logic.py +0 -0
  19. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/math.py +0 -0
  20. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/misc.py +0 -0
  21. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/navigation.py +0 -0
  22. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/strings.py +0 -0
  23. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/subsetting.py +0 -0
  24. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/invocations/types.py +0 -0
  25. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/nodes.py +0 -0
  26. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/engine/util.py +0 -0
  27. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/__init__.py +0 -0
  28. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/dstu2/choiceTypePaths.json +0 -0
  29. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/dstu2/path2Type.json +0 -0
  30. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/dstu2/pathsDefinedElsewhere.json +0 -0
  31. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/dstu2/type2Parent.json +0 -0
  32. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/r4/choiceTypePaths.json +0 -0
  33. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/r4/path2Type.json +0 -0
  34. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/r4/pathsDefinedElsewhere.json +0 -0
  35. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/r4/type2Parent.json +0 -0
  36. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/r5/choiceTypePaths.json +0 -0
  37. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/r5/path2Type.json +0 -0
  38. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/r5/pathsDefinedElsewhere.json +0 -0
  39. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/r5/type2Parent.json +0 -0
  40. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/stu3/choiceTypePaths.json +0 -0
  41. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/stu3/path2Type.json +0 -0
  42. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/stu3/pathsDefinedElsewhere.json +0 -0
  43. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/models/stu3/type2Parent.json +0 -0
  44. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/ASTPathListener.py +0 -0
  45. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/FHIRPath.g4 +0 -0
  46. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/README.md +0 -0
  47. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/__init__.py +0 -0
  48. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/generated/FHIRPath.interp +0 -0
  49. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/generated/FHIRPath.tokens +0 -0
  50. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/generated/FHIRPathLexer.interp +0 -0
  51. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/generated/FHIRPathLexer.py +0 -0
  52. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/generated/FHIRPathLexer.tokens +0 -0
  53. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/generated/FHIRPathListener.py +0 -0
  54. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/generated/FHIRPathParser.py +0 -0
  55. {fhirpathpy-2.1.0 → fhirpathpy-2.2.0}/fhirpathpy/parser/generated/__init__.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fhirpathpy
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: FHIRPath implementation in Python
5
5
  Keywords: fhir,fhirpath
6
6
  Author-email: "beda.software" <fhirpath@beda.software>
7
- Requires-Python: >=3.8
7
+ Requires-Python: >=3.10
8
8
  Description-Content-Type: text/markdown
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Environment :: Web Environment
@@ -12,17 +12,19 @@ Classifier: Intended Audience :: Developers
12
12
  Classifier: Operating System :: OS Independent
13
13
  Classifier: Programming Language :: Python
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
15
  Classifier: Programming Language :: Python :: 3.10
18
16
  Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Topic :: Internet :: WWW/HTTP
20
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
21
22
  License-File: LICENSE.md
22
23
  Requires-Dist: antlr4-python3-runtime~=4.10
23
24
  Requires-Dist: python-dateutil~=2.8
24
25
  Requires-Dist: pytest==7.1.1 ; extra == "test"
25
26
  Requires-Dist: pyyaml==5.4 ; extra == "test"
27
+ Requires-Dist: pydantic==2.13.1 ; extra == "test"
26
28
  Project-URL: Changelog, https://github.com/beda-software/fhirpath-py/blob/master/CHANGELOG.md
27
29
  Project-URL: Documentation, https://github.com/beda-software/fhirpath-py#readme
28
30
  Project-URL: Homepage, https://github.com/beda-software/fhirpath-py
@@ -35,7 +37,7 @@ fhirpath.py
35
37
  [![Build Status](https://github.com/beda-software/fhirpath-py/actions/workflows/build.yaml/badge.svg)](https://github.com/beda-software/fhirpath-py/actions)
36
38
  [![codecov](https://codecov.io/gh/beda-software/fhirpath-py/branch/master/graph/badge.svg)](https://codecov.io/gh/beda-software/fhirpath-py)
37
39
  [![pypi](https://img.shields.io/pypi/v/fhirpathpy.svg)](https://pypi.org/project/fhirpathpy/)
38
- [![Supported Python version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/release/python-380/)
40
+ [![Supported Python version](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/release/python-3100/)
39
41
 
40
42
  [FHIRPath](https://www.hl7.org/fhir/fhirpath.html) implementation in Python
41
43
 
@@ -4,7 +4,7 @@ fhirpath.py
4
4
  [![Build Status](https://github.com/beda-software/fhirpath-py/actions/workflows/build.yaml/badge.svg)](https://github.com/beda-software/fhirpath-py/actions)
5
5
  [![codecov](https://codecov.io/gh/beda-software/fhirpath-py/branch/master/graph/badge.svg)](https://codecov.io/gh/beda-software/fhirpath-py)
6
6
  [![pypi](https://img.shields.io/pypi/v/fhirpathpy.svg)](https://pypi.org/project/fhirpathpy/)
7
- [![Supported Python version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/release/python-380/)
7
+ [![Supported Python version](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/release/python-3100/)
8
8
 
9
9
  [FHIRPath](https://www.hl7.org/fhir/fhirpath.html) implementation in Python
10
10
 
@@ -1,18 +1,26 @@
1
- from fhirpathpy.engine.invocations.constants import constants
2
- from fhirpathpy.parser import parse
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Mapping, Sequence
4
+ from typing import Any, Protocol, TypeVar, cast
5
+
3
6
  from fhirpathpy.engine import do_eval
4
- from fhirpathpy.engine.util import arraify, get_data, set_paths, process_user_invocation_table
7
+ from fhirpathpy.engine.invocations.constants import constants
5
8
  from fhirpathpy.engine.nodes import FP_Type, ResourceNode
9
+ from fhirpathpy.engine.util import arraify, get_data, process_user_invocation_table, set_paths
10
+ from fhirpathpy.parser import parse
6
11
 
7
12
  __title__ = "fhirpathpy"
8
- __version__ = "2.1.0"
13
+ __version__ = "2.2.0"
9
14
  __author__ = "beda.software"
10
15
  __license__ = "MIT"
11
- __copyright__ = "Copyright 2025 beda.software"
16
+ __copyright__ = "Copyright 2026 beda.software"
12
17
 
13
18
  # Version synonym
14
19
  VERSION = __version__
15
20
 
21
+ ResourceType = Mapping[str, Any]
22
+ ContextType = Mapping[str, Any] | None
23
+
16
24
 
17
25
  def apply_parsed_path(resource, parsedPath, context=None, model=None, options=None):
18
26
  constants.reset()
@@ -84,7 +92,13 @@ def apply_parsed_path(resource, parsedPath, context=None, model=None, options=No
84
92
  return visit(node)
85
93
 
86
94
 
87
- def evaluate(resource, path, context=None, model=None, options=None):
95
+ def evaluate(
96
+ resource: ResourceType | list,
97
+ path: str | dict,
98
+ context: dict | None = None,
99
+ model: dict | None = None,
100
+ options: dict | None = None,
101
+ ) -> list:
88
102
  """
89
103
  Evaluates the "path" FHIRPath expression on the given resource, using data
90
104
  from "context" for variables mentioned in the "path" expression.
@@ -109,7 +123,9 @@ def evaluate(resource, path, context=None, model=None, options=None):
109
123
  return apply_parsed_path(resource, node, context or {}, model, options)
110
124
 
111
125
 
112
- def compile(path, model=None, options=None):
126
+ def compile(
127
+ path: str, model: dict | None = None, options: dict | None = None
128
+ ) -> Callable[[ResourceType, ContextType], list]:
113
129
  """
114
130
  Returns a function that takes a resource and an optional context hash (see
115
131
  "evaluate"), and returns the result of evaluating the given FHIRPath
@@ -124,3 +140,89 @@ def compile(path, model=None, options=None):
124
140
  For example, you could pass in the result of require("fhirpath/fhir-context/r4")
125
141
  """
126
142
  return set_paths(apply_parsed_path, parsedPath=parse(path), model=model, options=options)
143
+
144
+
145
+ InputType = TypeVar("InputType")
146
+ OutputType = TypeVar("OutputType")
147
+ # Contravariant: a callable accepting a wider input type is a valid subtype.
148
+ _I_contra = TypeVar("_I_contra", contravariant=True)
149
+ # Covariant: a callable returning a narrower output type is a valid subtype.
150
+ # Sequence (read-only) is required here because list is invariant and rejects covariant TypeVars.
151
+ _O_co = TypeVar("_O_co", covariant=True)
152
+
153
+
154
+ class CompiledFirst(Protocol[_I_contra, _O_co]):
155
+ def __call__(self, resource: _I_contra, context: ContextType = ...) -> _O_co | None: ...
156
+
157
+
158
+ class CompiledArray(Protocol[_I_contra, _O_co]):
159
+ def __call__(self, resource: _I_contra, context: ContextType = ...) -> Sequence[_O_co]: ...
160
+
161
+
162
+ def compile_as_array(
163
+ expression: str, input_type: type[InputType], output_type: type[OutputType]
164
+ ) -> CompiledArray[InputType, OutputType]:
165
+ path_fn = compile(expression)
166
+
167
+ def fn(resource: Any, context: ContextType = None) -> Any:
168
+ return _format_result(
169
+ path_fn(_prepare_data(resource, input_type), context),
170
+ output_type,
171
+ is_array=True,
172
+ )
173
+
174
+ return cast(CompiledArray[InputType, OutputType], fn)
175
+
176
+
177
+ def compile_as_first(
178
+ expression: str, input_type: type[InputType], output_type: type[OutputType]
179
+ ) -> CompiledFirst[InputType, OutputType]:
180
+ path_fn = compile(expression)
181
+
182
+ def fn(resource: Any, context: ContextType = None) -> Any:
183
+ return _format_result(
184
+ path_fn(_prepare_data(resource, input_type), context),
185
+ output_type,
186
+ is_array=False,
187
+ )
188
+
189
+ return cast(CompiledFirst[InputType, OutputType], fn)
190
+
191
+
192
+ def _prepare_data(resource: Any, input_type: type[InputType]) -> ResourceType:
193
+ if not isinstance(resource, input_type):
194
+ raise Exception(
195
+ f"Resource type is {type(resource).__name__}, expected {input_type.__name__}"
196
+ )
197
+
198
+ if isinstance(resource, dict):
199
+ return resource
200
+
201
+ if hasattr(resource, "model_dump"):
202
+ return resource.model_dump()
203
+
204
+ raise Exception(f"Don't know how to work with type {type(resource).__name__}")
205
+
206
+
207
+ def _format_result(result: list, output_type: type[OutputType], is_array=False) -> Any:
208
+ result = [_format_item(item, output_type) for item in result]
209
+
210
+ if is_array:
211
+ return result
212
+
213
+ if len(result) == 0:
214
+ return None
215
+
216
+ return result[0]
217
+
218
+
219
+ def _format_item(item: Any, output_type: type[OutputType]) -> OutputType:
220
+ if hasattr(output_type, "model_validate"):
221
+ return cast(Any, output_type).model_validate(item)
222
+
223
+ if not isinstance(item, output_type):
224
+ raise Exception(
225
+ f"Expected result to be {output_type.__name__}, but got {type(item).__name__}"
226
+ )
227
+
228
+ return item
File without changes
@@ -1,17 +1,3 @@
1
- [tool.black]
2
- line-length = 100
3
- target-version = ['py311']
4
- exclude = '''
5
- (
6
- /(
7
- | \.git
8
- | \.pytest_cache
9
- | pyproject.toml
10
- | dist
11
- )/
12
- )
13
- '''
14
-
15
1
  [tool.pytest.ini_options]
16
2
  minversion = "6.0"
17
3
  addopts = "-ra -q --color=yes --cov=fhirpathpy --cov-report=xml"
@@ -40,17 +26,18 @@ classifiers = [
40
26
  "Operating System :: OS Independent",
41
27
  "Programming Language :: Python",
42
28
  "Programming Language :: Python :: 3",
43
- "Programming Language :: Python :: 3.8",
44
- "Programming Language :: Python :: 3.9",
45
29
  "Programming Language :: Python :: 3.10",
46
30
  "Programming Language :: Python :: 3.11",
31
+ "Programming Language :: Python :: 3.12",
32
+ "Programming Language :: Python :: 3.13",
47
33
  "Topic :: Internet :: WWW/HTTP",
48
34
  "Topic :: Software Development :: Libraries :: Python Modules",
35
+ "Typing :: Typed",
49
36
  ]
50
- requires-python = ">=3.8"
37
+ requires-python = ">=3.10"
51
38
 
52
39
  [project.optional-dependencies]
53
- test = ["pytest==7.1.1", "pyyaml==5.4"]
40
+ test = ["pytest==7.1.1", "pyyaml==5.4", "pydantic==2.13.1"]
54
41
 
55
42
  [project.urls]
56
43
  Homepage = "https://github.com/beda-software/fhirpath-py"
@@ -60,17 +47,22 @@ Changelog = "https://github.com/beda-software/fhirpath-py/blob/master/CHANGELOG.
60
47
 
61
48
 
62
49
  [tool.ruff]
63
- target-version = "py39"
50
+ target-version = "py310"
64
51
  line-length = 100
65
- include = ["app/**/*.py", "tests/**/*.py"]
52
+ include = ["fhirpathpy/**/*.py", "tests/**/*.py"]
66
53
 
67
54
  [tool.ruff.lint]
68
55
  select = ["B", "F", "I", "E", "UP", "N", "PL", "PERF"]
69
- # Black is responsible for E501
70
56
  # N803/N806 is not relevant for us because we use camelCase for historical reasons
71
57
  ignore = ["E501", "N803", "N806"]
72
58
  unfixable = ["F401"]
73
59
 
60
+ [tool.mypy]
61
+ python_version = "3.10"
62
+ ignore_missing_imports = true
63
+ plugins = ["pydantic.mypy"]
64
+
65
+
74
66
  [tool.autohooks]
75
67
  mode = "pipenv"
76
- pre-commit = ["autohooks.plugins.black", "autohooks.plugins.ruff"]
68
+ pre-commit = ["autohooks.plugins.ruff.check", "autohooks.plugins.ruff.format"]
File without changes