Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete mypy type coverage for entire codebase #403

Merged
merged 2 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci-fmudataio.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
os: [ubuntu-latest]

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/fmudataio-documention.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ["3.8"]
python-version: ["3.10"]
os: [ubuntu-latest]

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/fmudataio-publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ jobs:
name: Build and publish Python 🐍 distributions 📦 to PyPI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Python 3.10
uses: actions/setup-python@v4
with:
python-version: "3.10"

- name: Install pypa/build
run: python -m pip install build twine

- name: Build package
run: python -m build . --sdist --wheel --outdir dist/

- name: Upload deploy
env:
TWINE_USERNAME: __token__
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ jobs:
matrix:
python-version: ["3.10"]
steps:
- uses: actions/checkout@v2

- uses: actions/checkout@v4
- name: Set up python
uses: actions/setup-python@v4
- name: Check black style and linting
run: pip install ruff
- name: Install dev-env.
run: |
pip install -U pip
pip install ".[dev]"
- name: Ruff check
if: ${{ always() }}
run: ruff check .
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Mypy

on: [push, pull_request]

jobs:
mypy:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.10"]
steps:
- uses: actions/checkout@v4

- name: Set up python
uses: actions/setup-python@v4

- name: Install dev-env.
run: |
pip install -U pip
pip install ".[dev]"

- name: Mypy
run: mypy .
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,9 @@ venv.bak/

# setuptools_scm version
src/fmu/dataio/version.py

# mypy
.dmypy.json

# Apple macOS
.DS_Store
11 changes: 8 additions & 3 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
[mypy]

[mypy-numpy.*]
# Applies to Python 3.6:
disallow_untyped_defs = True
exclude = ^((tests|docs|examples|build)/|conftest.py?)
extra_checks = True
ignore_missing_imports = True
python_version = 3.8
strict_equality = True
warn_redundant_casts = True
warn_unused_configs = True
warn_unused_ignores = True
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ dev = [
"rstcheck",
"ruff",
"termcolor",
"types-PyYAML",
]
docs = [
"autoapi",
Expand Down
69 changes: 45 additions & 24 deletions src/fmu/dataio/_definitions.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,65 @@
"""Various definitions and hard settings used in fmu-dataio."""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Final

SCHEMA = (
SCHEMA: Final = (
"https://main-fmu-schemas-prod.radix.equinor.com/schemas/0.8.0/fmu_results.json"
)
VERSION = "0.8.0"
SOURCE = "fmu"
VERSION: Final = "0.8.0"
SOURCE: Final = "fmu"


@dataclass
class _ValidFormats:
surface: dict = field(default_factory=dict)
grid: dict = field(default_factory=dict)
cube: dict = field(default_factory=dict)
table: dict = field(default_factory=dict)
polygons: dict = field(default_factory=dict)
points: dict = field(default_factory=dict)
dictionary: dict = field(default_factory=dict)

def __post_init__(self):
self.surface = {"irap_binary": ".gri"}
self.grid = {"hdf": ".hdf", "roff": ".roff"}
self.cube = {"segy": ".segy"}
self.table = {"hdf": ".hdf", "csv": ".csv", "arrow": ".arrow"}
self.polygons = {
surface: dict = field(
default_factory=lambda: {
"irap_binary": ".gri",
}
)
grid: dict = field(
default_factory=lambda: {
"hdf": ".hdf",
"roff": ".roff",
}
)
cube: dict = field(
default_factory=lambda: {
"segy": ".segy",
}
)
table: dict = field(
default_factory=lambda: {
"hdf": ".hdf",
"csv": ".csv",
"arrow": ".arrow",
}
)
polygons: dict = field(
default_factory=lambda: {
"hdf": ".hdf",
"csv": ".csv", # columns will be X Y Z, ID
"csv|xtgeo": ".csv", # use default xtgeo columns: X_UTME, ... POLY_ID
"irap_ascii": ".pol",
}
self.points = {
)
points: dict = field(
default_factory=lambda: {
"hdf": ".hdf",
"csv": ".csv", # columns will be X Y Z
"csv|xtgeo": ".csv", # use default xtgeo columns: X_UTME, Y_UTMN, Z_TVDSS
"irap_ascii": ".poi",
}
self.dictionary = {"json": ".json"}
)
dictionary: dict = field(
default_factory=lambda: {
"json": ".json",
}
)


ALLOWED_CONTENTS = {
ALLOWED_CONTENTS: Final = {
"depth": None,
"time": None,
"thickness": None,
Expand Down Expand Up @@ -71,14 +92,14 @@ def __post_init__(self):
"transmissibilities": None,
}

STANDARD_TABLE_INDEX_COLUMNS = {
STANDARD_TABLE_INDEX_COLUMNS: Final = {
"inplace_volumes": ["ZONE", "REGION", "FACIES", "LICENCE"],
"timeseries": ["DATE"], # summary
"rft": ["measured_depth", "well", "time"],
"wellpicks": ["WELL", "HORIZON"],
}

DEPRECATED_CONTENTS = {
DEPRECATED_CONTENTS: Final = {
"seismic": {
"offset": {
"replaced_by": "stacking_offset",
Expand All @@ -88,15 +109,15 @@ def __post_init__(self):

# This setting will set if subkeys is required or not. If not found in list then
# assume False.
CONTENTS_REQUIRED = {
CONTENTS_REQUIRED: Final = {
"fluid_contact": {"contact": True},
"field_outline": {"contact": False},
"field_region": {"id": True},
}

# This setting sets the FMU context for the output. If detected as a non-fmu run,
# the code will internally set actual_context=None
ALLOWED_FMU_CONTEXTS = {
ALLOWED_FMU_CONTEXTS: Final = {
"realization": "To realization-N/iter_M/share",
"case": "To casename/share, but will also work on project disk",
"case_symlink_realization": "To case/share, with symlinks on realizations level",
Expand Down
61 changes: 32 additions & 29 deletions src/fmu/dataio/_design_kw.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,30 @@
It is copied here instead of pip-installed in order to avoid dragging
along all dependencies of semeio"""

# pylint: disable=logging-fstring-interpolation
from __future__ import annotations

import logging
import re
import shlex
from typing import Any, Final, Iterable

_STATUS_FILE_NAME = "DESIGN_KW.OK"
_STATUS_FILE_NAME: Final = "DESIGN_KW.OK"

_logger = logging.getLogger(__name__)
_logger: Final = logging.getLogger(__name__)


def run(
template_file_name,
result_file_name,
log_level,
parameters_file_name="parameters.txt",
):
template_file_name: str,
result_file_name: str,
log_level: str,
parameters_file_name: str = "parameters.txt",
) -> None:
# Get all key, value pairs
# If FWL key is having multiple entries in the parameters file
# KeyError is raised. This will be logged, and no OK
# file is written

_logger.setLevel(log_level)

valid = True

with open(parameters_file_name) as parameters_file:
parameters = parameters_file.readlines()

Expand All @@ -40,24 +39,24 @@ def run(
with open(template_file_name) as template_file:
template = template_file.readlines()

if valid:
with open(result_file_name, "w") as result_file:
for line in template:
if not is_comment(line):
for key, value in key_vals.items():
line = line.replace(f"<{key}>", str(value))
valid = True
with open(result_file_name, "w") as result_file:
for line in template:
if not is_comment(line):
for key, value in key_vals.items():
line = line.replace(f"<{key}>", str(value))

if not all_matched(line, template_file_name, template):
valid = False
if not all_matched(line, template_file_name, template):
valid = False

result_file.write(line)
result_file.write(line)

if valid:
with open(_STATUS_FILE_NAME, "w") as status_file:
status_file.write("DESIGN_KW OK\n")


def all_matched(line, template_file_name, template):
def all_matched(line: str, template_file_name: str, template: list[str]) -> bool:
valid = True
for unmatched in unmatched_templates(line):
if is_perl(template_file_name, template):
Expand All @@ -73,24 +72,24 @@ def all_matched(line, template_file_name, template):
return valid


def is_perl(file_name, template):
return file_name.endswith(".pl") or template[0].find("perl") != -1
def is_perl(file_name: str, template: list[str]) -> bool:
return bool(file_name.endswith(".pl") or template[0].find("perl") != -1)


def unmatched_templates(line):
def unmatched_templates(line: str) -> list[str]:
bracketpattern = re.compile("<.+?>")
if bracketpattern.search(line):
return bracketpattern.findall(line)
return []


def is_comment(line):
def is_comment(line: str) -> bool:
ecl_comment_pattern = re.compile("^--")
std_comment_pattern = re.compile("^#")
return ecl_comment_pattern.search(line) or std_comment_pattern.search(line)
return bool(ecl_comment_pattern.search(line) or std_comment_pattern.search(line))


def extract_key_value(parameters):
def extract_key_value(parameters: Iterable[str]) -> dict[str, str]:
"""Parses a list of strings, looking for key-value pairs pr. line
separated by whitespace, into a dictionary.

Expand Down Expand Up @@ -128,7 +127,10 @@ def extract_key_value(parameters):
return res


def rm_genkw_prefix(paramsdict, ignoreprefixes="LOG10_"):
def rm_genkw_prefix(
paramsdict: dict[str, Any],
ignoreprefixes: str | list[str] | None = "LOG10_",
) -> dict[str, Any]:
"""Strip prefixes from keys in a dictionary.

Prefix is any string before a colon. No colon means no prefix.
Expand All @@ -152,7 +154,8 @@ def rm_genkw_prefix(paramsdict, ignoreprefixes="LOG10_"):
ignoreprefixes = []
if isinstance(ignoreprefixes, str):
ignoreprefixes = [ignoreprefixes]
ignoreprefixes = filter(None, ignoreprefixes)

ignoreprefixes = list(filter(None, ignoreprefixes))

for ignore_str in ignoreprefixes:
paramsdict = {
Expand Down
Loading
Loading