pyetp 0.0.39__py3-none-any.whl → 0.0.43a0__py3-none-any.whl

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.
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyetp
3
+ Version: 0.0.43a0
4
+ Summary: Interface with OSDU RDDMS using ETP protocol
5
+ Author-email: Adam Cheng <52572642+adamchengtkc@users.noreply.github.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: homepage, https://github.com/equinor/pyetp
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE.md
11
+ Requires-Dist: numpy>=1.26.3
12
+ Requires-Dist: websockets>=12.0
13
+ Requires-Dist: lxml>=4.9.4
14
+ Requires-Dist: pydantic>=1.10
15
+ Requires-Dist: async-timeout>=5.0
16
+ Requires-Dist: xtgeo>=4.0.0
17
+ Requires-Dist: xsdata>=24.3.1
18
+ Requires-Dist: etpproto>=1.0.7
19
+ Dynamic: license-file
20
+
21
+ ![Build Status](https://github.com/equinor/pyetp/actions/workflows/ci.yml/badge.svg?branch=main)
22
+ ![codecov](https://codecov.io/gh/equinor/pyetp/graph/badge.svg?token=S2XDDKKI8U)
23
+ ![Python](https://img.shields.io/pypi/pyversions/pyetp)
24
+ [![PyPI version](https://badge.fury.io/py/pyetp.svg)](https://badge.fury.io/py/pyetp)
25
+ ![License](https://img.shields.io/github/license/equinor/pyetp)
26
+
27
+ # Installing the library
28
+ This package is published to PyPI, and can be installed via:
29
+ ```bash
30
+ pip install pyetp
31
+ ```
32
+
33
+ ## Local development
34
+ Locally we suggest setting up a virtual environment, and installing the latest
35
+ version of pip. Then install the library in editable mode along with the
36
+ `dev`-dependency group. That is:
37
+ ```bash
38
+ python -m venv .venv
39
+ source .venv/bin/activate
40
+ pip install pip --upgrade
41
+ pip install -e .
42
+ pip install --group dev
43
+ ```
44
+
45
+
46
+ ## Linting and formatting
47
+ We use ruff as a linter and formatter. To lint run:
48
+ ```bash
49
+ ruff check
50
+ ```
51
+ To run the formatter do:
52
+ ```bash
53
+ ruff format
54
+ ```
55
+ Or if you just want to check what could have been formatted:
56
+ ```bash
57
+ ruff format --check
58
+ ```
59
+
60
+
61
+ # RESQML versions
62
+ The library is built and tested against RESQML v2.0.1. The spec can be
63
+ downloaded
64
+ [here](https://publications.opengroup.org/standards/energistics-standards/v231a).
65
+
66
+ # Generated Python objects from RESQML spec
67
+ Under `src/pyetp/resqml_objects` you will find Python objects generated from
68
+ the RESQML xml spec.
69
+
70
+ # Documentation
71
+ See `/examples` for 2D grid usage
72
+
73
+ `tests/test_mesh.py` for Unstructured/structured mesh
74
+
75
+ # Running the unit tests
76
+ We have set up unit tests against a local open-etp-server. To start this server
77
+ run:
78
+ ```bash
79
+ docker compose -f tests/compose.yml up [--detach]
80
+ ```
81
+ If you want to re-use the same terminal window you should use the
82
+ `--detach`-option, otherwise start a new terminal. We use `pytest` for testing,
83
+ which can be run via:
84
+ ```bash
85
+ py.test
86
+ ```
87
+
88
+ # This library is under active development and subject to breaking changes
@@ -0,0 +1,21 @@
1
+ pyetp/__init__.py,sha256=Vu3_qz0AazlD4Q6ZLGdQVqZ7lhzqNLivpQRS3owD8SY,251
2
+ pyetp/_version.py,sha256=b-D25r0F2ojY_bfYgOb171Q-k4AtL4xVxGGim3FNCxk,714
3
+ pyetp/client.py,sha256=kZsPi2OPvbQKX6IVoAsrdZ9_ZDbZYEhHuHOhfbf3XxQ,37427
4
+ pyetp/config.py,sha256=uGEx6n-YF7Rtgwckf0ovRKNOKgCUsiQx4IA0Tyiqafk,870
5
+ pyetp/resqml_objects.py,sha256=j00e8scSh-yYv4Lpp9WjzLiaKqJWd5Cs7ROcJcxxFVw,50721
6
+ pyetp/types.py,sha256=zOfUzEQcgBvveWJyM6dD7U3xJ4SCkWElewNL0Ml-PPY,6761
7
+ pyetp/uri.py,sha256=Y05vuTO-XOurgDavBeoPGOidALoCKjCBIb7YHmfbAco,3115
8
+ pyetp/utils_arrays.py,sha256=bLrb8H8TMjbpNLo0Zb9xYRUnM2W4BOf7WxOxWK4fcTc,10218
9
+ pyetp/utils_xml.py,sha256=i11Zv2PuxW-ejEXxfGPYHvuJv7EHPYXwvgoe1U8fOUM,6826
10
+ pyetp-0.0.43a0.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
11
+ resqml_objects/__init__.py,sha256=ilimTrQScFryxHrfZwZURVpW0li19bVnayUcHR7S_Fs,183
12
+ resqml_objects/epc_readers.py,sha256=InYMlwjiZZRG9poQlWKFEOUNJmowGahXoNu_GniOaBw,3458
13
+ resqml_objects/parsers.py,sha256=UZdBi3QmBq4ejwKI9Dse_lMqL5Bx2s3QNTs8TKS4fO0,427
14
+ resqml_objects/serializers.py,sha256=JqRO6D6ExT5DrVyiNwBgNW28108f6spvxiVqJU0D9mc,352
15
+ resqml_objects/v201/__init__.py,sha256=yL3jWgkyGAzr-wt_WJDV_eARE75NoA6SPEKBrKM4Crk,51630
16
+ resqml_objects/v201/generated.py,sha256=Se0eePS6w25sfmnp2UBSkgzDGJ9c9Y2QqJgDRUTt_-Q,769527
17
+ resqml_objects/v201/utils.py,sha256=WiywauiJRBWhdjUvbKhpltRjoBX3qWd7qQ0_FAmIzUc,1442
18
+ pyetp-0.0.43a0.dist-info/METADATA,sha256=l1HO7RymATEmwlG6PCYhjLjzeBdl8Aj5qRd5pwtmrwY,2589
19
+ pyetp-0.0.43a0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ pyetp-0.0.43a0.dist-info/top_level.txt,sha256=NrdXbidkT5QR4NjH6nv2Frixknqse3jZq7bnqNdVb5k,21
21
+ pyetp-0.0.43a0.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ pyetp
2
+ resqml_objects
@@ -0,0 +1,7 @@
1
+ from .parsers import parse_resqml_v201_object
2
+ from .serializers import serialize_resqml_v201_object
3
+
4
+ __all__ = [
5
+ "parse_resqml_v201_object",
6
+ "serialize_resqml_v201_object",
7
+ ]
@@ -0,0 +1,114 @@
1
+ import logging
2
+ import pathlib
3
+ import sys
4
+ import zipfile
5
+
6
+ import h5py
7
+ import numpy as np
8
+ import numpy.typing as npt
9
+
10
+ import resqml_objects.v201 as ro_201
11
+
12
+ from .parsers import parse_resqml_v201_object
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def get_resqml_v201_objects(
18
+ epc_filename: str | pathlib.Path, log_failed_objects: bool = False
19
+ ) -> list[ro_201.AbstractObject]:
20
+ """This function is fairly brute-force; the function reads all files in the
21
+ provided EPC-file, and tries to parse it as one of the auto-generated
22
+ RESQML v2.0.1 objects. If you are unsure that all objects were included you
23
+ should set `log_failed_objects=True` and see if there are any missed
24
+ RESQML-objects. Objects such as `[Content_Types].xml`, `docProps/core.xml`,
25
+ and anything under the `_rels/`-directory are not RESQML-objects, and will
26
+ be logged as failed.
27
+
28
+
29
+ Parameters
30
+ ----------
31
+ epc_filename: str | pathlib.Path
32
+ The name of the EPC-file. Typically with an `.epc`-extension.
33
+ log_failed_objects: bool
34
+ Set to `True` in order to log the objects that were not parsed as
35
+ RESQML v2.0.1 objects along with their errors. Default is `False`.
36
+
37
+
38
+ Returns
39
+ -------
40
+ list[ro_201.AbstractObject]
41
+ A list of the parsed RESQML v2.0.1 objects.
42
+
43
+
44
+ See Also
45
+ --------
46
+ get_arrays_and_paths_in_hdf_file
47
+ """
48
+
49
+ if sys.version_info.major == 3 and sys.version_info.minor == 10:
50
+ raise NotImplementedError(
51
+ "The epc-reader 'get_resqml_v201_objects'-function does not work for "
52
+ "Python 3.10. Consider switching to Python 3.11 or up."
53
+ )
54
+
55
+ robjs = []
56
+ fail = {}
57
+
58
+ with zipfile.ZipFile(epc_filename, "r") as zf:
59
+ for zi in zf.infolist():
60
+ with zf.open(zi.filename) as f:
61
+ c = f.read()
62
+
63
+ try:
64
+ robjs.append(parse_resqml_v201_object(c))
65
+ except AttributeError as e:
66
+ fail[zi.filename] = e
67
+
68
+ if log_failed_objects:
69
+ logger.info(f"Failed to parse: {fail}")
70
+
71
+ return robjs
72
+
73
+
74
+ def get_arrays_and_paths_in_hdf_file(
75
+ epc_hdf_filename: str | pathlib.Path,
76
+ ) -> dict[str, npt.NDArray[np.bool_ | np.number | np.character]]:
77
+ """In this function we read all arrays and their full paths in an
78
+ HDF5-file. In the context of an EPC-file this corresponds to the
79
+ `pathInHdfFile`-attribute in a RESQML/WITSML/PRODML-object that has
80
+ connected array data.
81
+
82
+
83
+ Parameters
84
+ ----------
85
+ epc_hdf_filename: str | pathlib.Path
86
+ The filename of the HDF5-file that is connected to an EPC-file. This is
87
+ typically the same filename as the EPC-file, but with the
88
+ `.epc`-extension replaced by `.h5`.
89
+
90
+
91
+ Returns
92
+ -------
93
+ dict[str | npt.NDArray[np.bool_ | np.number | np.character]
94
+ A dictionary mapping the `pathInHdfFile`-key to the actual array as a
95
+ NumPy-array.
96
+
97
+
98
+ See Also
99
+ --------
100
+ get_resqml_v201_objects
101
+ """
102
+ datasets = {}
103
+
104
+ def populate_datasets(name: str, obj: h5py.Dataset | h5py.Group) -> None:
105
+ if isinstance(obj, h5py.Dataset):
106
+ # The name does not include the top level group "/". This creates a
107
+ # mismatch between the PathInHdfFile-element in the RESQML
108
+ # documents.
109
+ datasets["/" + name] = np.array(obj)
110
+
111
+ with h5py.File(epc_hdf_filename, "r") as f:
112
+ f.visititems(populate_datasets)
113
+
114
+ return datasets
@@ -0,0 +1,12 @@
1
+ from lxml import etree
2
+ from xsdata.formats.dataclass.context import XmlContext
3
+ from xsdata.formats.dataclass.parsers import XmlParser
4
+
5
+ import resqml_objects.v201 as ro_201
6
+
7
+
8
+ def parse_resqml_v201_object(raw_data: bytes) -> ro_201.AbstractObject:
9
+ parser = XmlParser(context=XmlContext())
10
+
11
+ obj_name = etree.QName(etree.fromstring(raw_data).tag).localname
12
+ return parser.from_bytes(raw_data, getattr(ro_201, obj_name))
@@ -0,0 +1,10 @@
1
+ from xsdata.formats.dataclass.serializers import XmlSerializer
2
+ from xsdata.formats.dataclass.serializers.config import SerializerConfig
3
+
4
+ import resqml_objects.v201 as ro_201
5
+
6
+
7
+ def serialize_resqml_v201_object(obj: ro_201.AbstractObject) -> bytes:
8
+ serializer = XmlSerializer(config=SerializerConfig())
9
+
10
+ return str.encode(serializer.render(obj))