Skip to content

Commit 02f48e4

Browse files
authored
Merge pull request #59 from MiraGeoscience/hotfix/0.2.2
Hotfix/0.2.2
2 parents 0f5b79c + e7bc9ec commit 02f48e4

File tree

6 files changed

+131
-109
lines changed

6 files changed

+131
-109
lines changed

.github/workflows/python_analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,6 @@ jobs:
4444
uses: MiraGeoscience/CI-tools/.github/workflows/reusable-python-pytest_unix_os.yml@main
4545
with:
4646
package_manager: 'poetry'
47-
python_ver: '["3.10", "3.11", "3.12"]'
47+
python_ver: '["3.10"]'
4848
os: '["ubuntu-latest"]'
4949
cache_number: 1

docs/source/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@
2727
html_static_path = ["_static"]
2828

2929
# The short X.Y version.
30-
version = "0.2.1"
30+
version = "0.2.2"
3131
# The full version, including alpha/beta/rc tags.
32-
release = "0.2.1"
32+
release = "0.2.2"

las_geoh5/__init__.py

Lines changed: 7 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@
66
# (see LICENSE file at the root of this source code package).
77
#
88

9-
# flake8: noqa
10-
119
from __future__ import annotations
12-
import re
10+
1311
from pathlib import Path
14-
from lasio import reader
1512

16-
__version__ = "0.2.1"
13+
14+
__version__ = "0.2.2"
1715

1816

1917
def assets_path() -> Path:
@@ -28,95 +26,7 @@ def assets_path() -> Path:
2826
return assets_folder
2927

3028

31-
# TODO: Propose change on lasio to fix possible version issue
32-
33-
34-
def configure_metadata_patterns(line, section_name): # pylint: disable=too-many-locals
35-
"""Configure regular-expression patterns to parse section meta-data lines.
36-
37-
# OVERLOAD lasio.reader.configure_metadata_patterns
38-
39-
Arguments:
40-
line (str): line from LAS header section
41-
section_name (str): Name of the section the 'line' is from.
42-
43-
Returns:
44-
An array of regular-expression strings (patterns).
45-
"""
46-
47-
# Default return value
48-
patterns = []
49-
50-
# Default regular expressions for name, value and desc fields
51-
name_re = r"\.?(?P<name>[^.]*)\."
52-
value_re = r"(?P<value>.*):"
53-
desc_re = r"(?P<descr>.*)"
54-
55-
# Default regular expression for unit field. Note that we
56-
# attempt to match "1000 psi" as a special case which allows
57-
# a single whitespace character, in contradiction to the LAS specification
58-
# See GitHub issue #363 for details.
59-
if "VERS" in line:
60-
unit_re = r"(?P<unit>\D*)"
61-
else:
62-
unit_re = r"(?P<unit>([0-9]+\s)?[^\s]*)"
63-
64-
# Alternate regular expressions for special cases
65-
name_missing_period_re = r"(?P<name>[^:]*):"
66-
value_missing_period_re = r"(?P<value>.*)"
67-
value_without_colon_delimiter_re = r"(?P<value>[^:]*)"
68-
value_with_time_colon_re = (
69-
r"(?P<value>.*?)(?:(?<!( [0-2][0-3]| hh| HH)):(?!([0-5][0-9]|mm|MM)))"
70-
)
71-
name_with_dots_re = r"\.?(?P<name>[^.].*[.])\."
72-
no_desc_re = ""
73-
no_unit_re = ""
74-
75-
# Configure special cases
76-
# 1. missing period (assume that only name and value are present)
77-
# 2. missing colon delimiter and description field
78-
# 3. double_dots '..' caused by mnemonic abbreviation (with period)
79-
# next to the dot delimiter.
80-
if ":" in line:
81-
if not "." in line[: line.find(":")]:
82-
# If there is no period, then we assume that the colon exists and
83-
# everything on the left is the name, and everything on the right
84-
# is the value - therefore no unit or description field.
85-
name_re = name_missing_period_re
86-
value_re = value_missing_period_re
87-
desc_re = no_desc_re
88-
unit_re = no_unit_re
89-
value_with_time_colon_re = value_missing_period_re
90-
91-
if not ":" in line:
92-
# If there isn't a colon delimiter then there isn't
93-
# a description field either.
94-
value_re = value_without_colon_delimiter_re
95-
desc_re = no_desc_re
96-
97-
if ".." in line and section_name == "Curves":
98-
name_re = name_with_dots_re
99-
else:
100-
if re.search(r"[^ ]\.\.", line) and section_name == "Curves":
101-
double_dot = line.find("..")
102-
desc_colon = line.rfind(":")
103-
104-
# Check that a double_dot is not in the
105-
# description string.
106-
if double_dot < desc_colon:
107-
name_re = name_with_dots_re
108-
109-
if section_name == "Parameter":
110-
# Search for a value entry with a time-value first.
111-
pattern = name_re + unit_re + value_with_time_colon_re + desc_re
112-
patterns.append(pattern)
113-
114-
# Add the regular pattern for all section_names
115-
# for the Parameter section this will run after time-value pattern
116-
pattern = name_re + unit_re + value_re + desc_re
117-
patterns.append(pattern)
118-
119-
return patterns
120-
121-
122-
reader.configure_metadata_patterns = configure_metadata_patterns
29+
__all__ = [
30+
"__version__",
31+
"assets_path",
32+
]

las_geoh5/import_files/driver.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@
1717
from pathlib import Path
1818
from shutil import move
1919

20-
import lasio
2120
from geoh5py import Workspace
2221
from geoh5py.shared.utils import fetch_active_workspace
2322
from geoh5py.ui_json import InputFile
2423
from tqdm import tqdm
2524

2625
from las_geoh5.import_files.params import ImportOptions, NameOptions
27-
from las_geoh5.import_las import las_to_drillhole
26+
from las_geoh5.import_las import las_to_drillhole, lasio_read
2827

2928

3029
_logger = logging.getLogger(__name__)
@@ -113,11 +112,7 @@ def run(params_json: Path, output_geoh5: Path | None = None):
113112
for file in tqdm(
114113
ifile.data["files"].split(";"), desc="Reading LAS files"
115114
):
116-
futures.append(
117-
pool.apply_async(
118-
lasio.read, (file,), {"mnemonic_case": "preserve"}
119-
)
120-
)
115+
futures.append(pool.apply_async(lasio_read, (file,)))
121116

122117
lasfiles = [future.get() for future in futures]
123118

las_geoh5/import_las.py

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from __future__ import annotations
1010

1111
import logging
12+
import re
1213
from pathlib import Path
1314
from typing import Any
1415

@@ -24,6 +25,9 @@
2425
from las_geoh5.import_files.params import ImportOptions, NameOptions
2526

2627

28+
_logger = logging.getLogger(__name__)
29+
30+
2731
class LASTranslator:
2832
"""Translator for the weakly standardized LAS file standard."""
2933

@@ -182,7 +186,7 @@ def add_survey(
182186
survey = Path(survey)
183187

184188
if survey.suffix == ".las":
185-
file = lasio.read(survey, mnemonic_case="preserve")
189+
file = lasio_read(survey)
186190
try:
187191
surveys = np.c_[get_depths(file)["depth"], file["DIP"], file["AZIM"]]
188192
if len(drillhole.surveys) == 1:
@@ -416,3 +420,116 @@ def las_to_drillhole(
416420
new_row = drillhole.surveys[0, :]
417421
new_row[0] = np.max(depths)
418422
drillhole.surveys = np.vstack([drillhole.surveys, new_row])
423+
424+
425+
def _patch_lasio_reader():
426+
"""Patch lasio.reader.configure_metadata_patterns to handle edge cases."""
427+
428+
# patch only once
429+
if getattr(lasio.reader, "patched_configure_metadata_patterns", False):
430+
return
431+
432+
_logger.debug("Patching lasio.reader.configure_metadata_patterns")
433+
434+
# TODO: Propose change on lasio to fix possible version issue
435+
436+
def configure_metadata_patterns(line, section_name): # pylint: disable=too-many-locals
437+
"""Configure regular-expression patterns to parse section meta-data lines.
438+
439+
# OVERLOAD lasio.reader.configure_metadata_patterns
440+
441+
Arguments:
442+
line (str): line from LAS header section
443+
section_name (str): Name of the section the 'line' is from.
444+
445+
Returns:
446+
An array of regular-expression strings (patterns).
447+
"""
448+
449+
# Default return value
450+
patterns = []
451+
452+
# Default regular expressions for name, value and desc fields
453+
name_re = r"\.?(?P<name>[^.]*)\."
454+
value_re = r"(?P<value>.*):"
455+
desc_re = r"(?P<descr>.*)"
456+
457+
# Default regular expression for unit field. Note that we
458+
# attempt to match "1000 psi" as a special case which allows
459+
# a single whitespace character, in contradiction to the LAS specification
460+
# See GitHub issue #363 for details.
461+
if "VERS" in line:
462+
unit_re = r"(?P<unit>\D*)"
463+
else:
464+
unit_re = r"(?P<unit>([0-9]+\s)?[^\s]*)"
465+
466+
# Alternate regular expressions for special cases
467+
name_missing_period_re = r"(?P<name>[^:]*):"
468+
value_missing_period_re = r"(?P<value>.*)"
469+
value_without_colon_delimiter_re = r"(?P<value>[^:]*)"
470+
value_with_time_colon_re = (
471+
r"(?P<value>.*?)(?:(?<!( [0-2][0-3]| hh| HH)):(?!([0-5][0-9]|mm|MM)))"
472+
)
473+
name_with_dots_re = r"\.?(?P<name>[^.].*[.])\."
474+
no_desc_re = ""
475+
no_unit_re = ""
476+
477+
# Configure special cases
478+
# 1. missing period (assume that only name and value are present)
479+
# 2. missing colon delimiter and description field
480+
# 3. double_dots '..' caused by mnemonic abbreviation (with period)
481+
# next to the dot delimiter.
482+
if ":" in line:
483+
if "." not in line[: line.find(":")]:
484+
# If there is no period, then we assume that the colon exists and
485+
# everything on the left is the name, and everything on the right
486+
# is the value - therefore no unit or description field.
487+
name_re = name_missing_period_re
488+
value_re = value_missing_period_re
489+
desc_re = no_desc_re
490+
unit_re = no_unit_re
491+
value_with_time_colon_re = value_missing_period_re
492+
493+
if ":" not in line:
494+
# If there isn't a colon delimiter then there isn't
495+
# a description field either.
496+
value_re = value_without_colon_delimiter_re
497+
desc_re = no_desc_re
498+
499+
if ".." in line and section_name == "Curves":
500+
name_re = name_with_dots_re
501+
else:
502+
if re.search(r"[^ ]\.\.", line) and section_name == "Curves":
503+
double_dot = line.find("..")
504+
desc_colon = line.rfind(":")
505+
506+
# Check that a double_dot is not in the
507+
# description string.
508+
if double_dot < desc_colon:
509+
name_re = name_with_dots_re
510+
511+
if section_name == "Parameter":
512+
# Search for a value entry with a time-value first.
513+
pattern = name_re + unit_re + value_with_time_colon_re + desc_re
514+
patterns.append(pattern)
515+
516+
# Add the regular pattern for all section_names
517+
# for the Parameter section this will run after time-value pattern
518+
pattern = name_re + unit_re + value_re + desc_re
519+
patterns.append(pattern)
520+
521+
return patterns
522+
523+
lasio.reader.configure_metadata_patterns = configure_metadata_patterns
524+
lasio.reader.patched_configure_metadata_patterns = True
525+
526+
527+
def lasio_read(file):
528+
"""Read a LAS file using lasio.
529+
530+
Wrapper around lasio.read that patches the reader to handle some
531+
edge cases in LAS files.
532+
"""
533+
534+
_patch_lasio_reader()
535+
return lasio.read(file, mnemonic_case="preserve")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "las-geoh5"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
description = "Las/Geoh5 conversion"
55
license = "MIT"
66
readme = "README.rst"

0 commit comments

Comments
 (0)