Skip to content
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
2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def about_package(init_posixpath: pathlib.Path) -> dict:

# Override link in 'Edit on Github'
rst_prolog = f"""
:github_url: {ABOUT_TFS['__url__']}
:github_url: {ABOUT_TFS["__url__"]}
"""

# The version info for the project you're documenting, acts as replacement for
Expand Down
59 changes: 55 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,7 @@ repository = "https://github.com/pylhc/tfs"
documentation = "https://pylhc.github.io/tfs/ "
changelog = "https://github.com/pylhc/tfs/blob/master/CHANGELOG.md"


[tool.ruff]
target-version = "py310" # Assume Python 3.10+
# ----- Tests Configuration ----- #

[tool.pytest.ini_options]
addopts = "--cov-report=xml --cov-report term-missing --cov-config=pyproject.toml --cov=tfs"
Expand All @@ -98,4 +96,57 @@ testpaths = ["tests"]
exclude_also = [
"if TYPE_CHECKING:", # do not count type checking imports (ignored at runtime) for coverage
"except ImportError:", # do not count missing optional dependencies set to None, we monkeypatch and test that
]
]

# ----- Dev Tools Configuration ----- #

[tool.ruff]
exclude = [
".eggs",
".git",
".mypy_cache",
".venv",
"_build",
"build",
"dist",
]

# Assume Python 3.10+
target-version = "py310"

line-length = 110
indent-width = 4

[tool.ruff.lint]
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
ignore = [
"E501", # line too long
"FBT001", # boolean-type-hint-positional-argument
"FBT002", # boolean-default-value-positional-argument
"PT019", # pytest-fixture-param-without-value (but suggested solution fails)
]
extend-select = [
"F", # Pyflakes rules
"W", # PyCodeStyle warnings
"E", # PyCodeStyle errors
"I", # Sort imports properly
"A", # Detect shadowed builtins
"N", # enforce naming conventions, e.g. ClassName vs function_name
"UP", # Warn if certain things can changed due to newer Python versions
"C4", # Catch incorrect use of comprehensions, dict, list, etc
"FA", # Enforce from __future__ import annotations
"FBT", # detect boolean traps
"ISC", # Good use of string concatenation
"BLE", # disallow catch-all exceptions
"ICN", # Use common import conventions
"RET", # Good return practices
"SIM", # Common simplification rules
"TID", # Some good import practices
"TC", # Enforce importing certain types in a TYPE_CHECKING block
"PTH", # Use pathlib instead of os.path
"NPY", # Some numpy-specific things
]
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
33 changes: 19 additions & 14 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,46 +47,50 @@ def _tfs_madng_file() -> pathlib.Path:

@pytest.fixture
def _pd_dataframe() -> pd.DataFrame:
rng = np.random.default_rng()
return pd.DataFrame(
index=range(3),
columns="a b c d e".split(),
data=np.random.rand(3, 5),
columns=["a", "b", "c", "d", "e"],
data=rng.random(size=(3, 5)),
)


@pytest.fixture
def _tfs_dataframe() -> TfsDataFrame:
rng = np.random.default_rng()
return TfsDataFrame(
index=range(15),
columns="a b c d e".split(),
data=np.random.rand(15, 5),
columns=["a", "b", "c", "d", "e"],
data=rng.random(size=(15, 5)),
headers={"Title": "Tfs Title", "Value": 3.3663},
)


@pytest.fixture
def _tfs_dataframe_booleans() -> TfsDataFrame:
"""TfsDataFrame with boolean values in the headers and data (1 column)."""
rng = np.random.default_rng()
df = TfsDataFrame(
index=range(15),
columns="a b c d e".split(),
data=np.random.rand(15, 5),
columns=["a", "b", "c", "d", "e"],
data=rng.random(size=(15, 5)),
headers={"Title": "Bool Test", "Bool1": True, "Bool2": False, "Bool3": 1},
)
df["bools"] = np.random.rand(15) > 0.5 # random from 0 to 1 and then boolean check
df["bools"] = rng.random(15) > 0.5 # random from 0 to 1 and then boolean check
return df


@pytest.fixture
def _tfs_dataframe_complex() -> TfsDataFrame:
"""TfsDataFrame with complex values in the headers and data (1 column)."""
rng = np.random.default_rng()
df = TfsDataFrame(
index=range(15),
columns="a b c d e".split(),
data=np.random.rand(15, 5),
columns=["a", "b", "c", "d", "e"],
data=rng.random(size=(15, 5)),
headers={"Title": "Complex Test", "Complex1": 1 + 2j, "Complex2": -4 - 17.9j},
)
df["complex"] = np.random.rand(15) + np.random.rand(15) * 1j
df["complex"] = rng.random(15) + rng.random(15) * 1j
return df


Expand All @@ -96,10 +100,11 @@ def _tfs_dataframe_madng() -> TfsDataFrame:
TfsDataFrame with both booleans and complex
values in the headers and data (1 column each).
"""
rng = np.random.default_rng()
df = TfsDataFrame(
index=range(15),
columns="a b c d e".split(),
data=np.random.rand(15, 5),
columns=["a", "b", "c", "d", "e"],
data=rng.random(size=(15, 5)),
headers={
"Title": "MADNG Test",
"Bool1": True,
Expand All @@ -109,6 +114,6 @@ def _tfs_dataframe_madng() -> TfsDataFrame:
"Complex2": -94.6 - 67.9j,
},
)
df["bools"] = np.random.rand(15) > 0.5 # random from 0 to 1 and then boolean check
df["complex"] = np.random.rand(15) + np.random.rand(15) * 1j
df["bools"] = rng.random(15) > 0.5 # random from 0 to 1 and then boolean check
df["complex"] = rng.random(15) + rng.random(15) * 1j
return df
11 changes: 5 additions & 6 deletions tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def _get_filename(self, template, plane=""):


class TestRead:

def test_read_pathlib_input(
self, _input_dir_pathlib: pathlib.Path, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame
):
Expand All @@ -48,13 +47,13 @@ def test_read_str_input(self, _input_dir_str: str, _tfs_x: TfsDataFrame, _tfs_y:


class TestWrite:

def test_write(self, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame, tmp_path):
c = CollectionTest(tmp_path)
file_x_path = tmp_path / "nofile_x.tfs"
assert not file_x_path.is_file()

c.nofile_x = _tfs_y # only assigns dataframe without writing (use _tfs_y so that we can set _tfs_x below)
# only assigns dataframe without writing (use _tfs_y so that we can set _tfs_x below)
c.nofile_x = _tfs_y
assert not file_x_path.is_file()
assert_tfs_frame_equal(_tfs_y, c.nofile_x)

Expand Down Expand Up @@ -130,7 +129,9 @@ def test_buffer_flush(self, _input_dir_str: str, _tfs_x: TfsDataFrame, _tfs_y: T
assert tfs_x_after_flush.loc["BPMSX.4L2.B1", "NUMBER"] == -199
assert tfs_y_after_flush.loc["BPMSX.4L2.B1", "NUMBER"] == -19

def test_buffer_flush_nowrite(self, _input_dir_str: str, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame, tmp_path):
def test_buffer_flush_nowrite(
self, _input_dir_str: str, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame, tmp_path
):
c = CollectionTest(tmp_path, allow_write=True)

c.file_x = _tfs_x.copy()
Expand All @@ -155,7 +156,6 @@ def test_buffer_flush_nowrite(self, _input_dir_str: str, _tfs_x: TfsDataFrame, _


class TestFilenames:

def test_tfscollection_getfilename_not_implemented(self):
with pytest.raises(NotImplementedError):
TfsCollection._get_filename("doesnt matter") # noqa: SLF001
Expand Down Expand Up @@ -206,7 +206,6 @@ def test_get_path(self, _input_dir_pathlib: pathlib.Path):


class TestOther:

def test_access_methods(self, _input_dir_pathlib: pathlib.Path):
c = CollectionTest(_input_dir_pathlib, allow_write=False)

Expand Down
3 changes: 2 additions & 1 deletion tests/test_compression.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

from .conftest import INPUTS_DIR

SUPPORTED_EXTENSIONS: tuple[str] = ["gz", "bz2", "zip", "xz", "zst", "tar", "tar.gz"] # through pandas
# Compression extensions supported through pandas
SUPPORTED_EXTENSIONS: tuple[str] = ("gz", "bz2", "zip", "xz", "zst", "tar", "tar.gz")

# ----- Compression tests with 'classic' TFS files (no MAD-NG features) ----- #

Expand Down
11 changes: 10 additions & 1 deletion tests/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,16 @@ def test_header_print(self):
assert str(val) in print_out

def test_long_headers_print(self):
headers = {"p1": 1, "p2": "hello", "p3": 3, "p4": 4, "p5": 5, "p6": 6, "p7": "string", "p8": "long"}
headers = {
"p1": 1,
"p2": "hello",
"p3": 3,
"p4": 4,
"p5": 5,
"p6": 6,
"p7": "string",
"p8": "long",
}
df = TfsDataFrame(headers=headers)
print_out = str(df)
assert "Headers" in print_out
Expand Down
8 changes: 6 additions & 2 deletions tests/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ def test_tfs_read_write_read_pathlib_input(self, _tfs_filex: pathlib.Path, tmp_p

def test_read_write_wise_header(self, _tfs_file_wise, tmp_path):
original_text = _tfs_file_wise.read_text()
original_header_lines = [line for line in original_text.splitlines() if line.strip().startswith(HEADER)]
original_header_lines = [
line for line in original_text.splitlines() if line.strip().startswith(HEADER)
]
df = read_tfs(_tfs_file_wise)

assert len(df.headers) == len(original_header_lines)
Expand Down Expand Up @@ -111,7 +113,9 @@ def test_read_file_with_empty_lines_in_header(self, _tfs_file_empty_lines, _tfs_
df_for_compare = read_tfs(_tfs_filex)
assert_tfs_frame_equal(df, df_for_compare)

def test_read_file_single_header_empty_line_in_header(self, _tfs_file_single_header_empty_line, _tfs_filex):
def test_read_file_single_header_empty_line_in_header(
self, _tfs_file_single_header_empty_line, _tfs_filex
):
"""Very special, but this was a case that failed in the past."""
df = read_tfs(_tfs_file_single_header_empty_line)
assert len(df.headers) == 1
Expand Down
1 change: 0 additions & 1 deletion tests/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@


class TestAssertTfsDataFrameEqual:

def test_no_headers_equal(self):
df1 = TfsDataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
assert_tfs_frame_equal(df1, df1) # we expect True
Expand Down
17 changes: 12 additions & 5 deletions tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ def test_validate_raises_on_wrong_unique_behavior(self, validation_mode):
validate(df, "", non_unique_behavior="invalid", compatibility=validation_mode)

@pytest.mark.parametrize("validation_mode", ["madx", "mad-x", "madng", "MAD-NG"])
def test_validation_raises_space_in_colname(self, _space_in_colnames_tfs_path: pathlib.Path, validation_mode):
def test_validation_raises_space_in_colname(
self, _space_in_colnames_tfs_path: pathlib.Path, validation_mode
):
# Read file has a space in a column name which should raise
with pytest.raises(SpaceinColumnNameError, match="TFS-Columns can not contain spaces."):
_ = read_tfs(_space_in_colnames_tfs_path, index="NAME", validate=validation_mode)
Expand Down Expand Up @@ -124,22 +126,26 @@ class TestMADXFailures:
def test_madx_validation_raises_if_no_headers(self, _pd_dataframe, validation_mode):
"""MAD-X expects at least a 'TYPE' header. If there are no headers, we raise."""
df = _pd_dataframe
with pytest.raises(MADXCompatibilityError, match="Headers should be present in MAD-X compatibility mode"):
with pytest.raises(
MADXCompatibilityError, match="Headers should be present in MAD-X compatibility mode"
):
validate(df, compatibility=validation_mode)

@pytest.mark.parametrize("validation_mode", ["madx", "mad-x", "mAd-X"])
def test_madx_validation_raises_on_boolean_headers(self, _tfs_booleans_file, validation_mode):
df = read_tfs(_tfs_booleans_file)
with pytest.raises(
MADXCompatibilityError, match="TFS-Headers can not contain boolean values in MAD-X compatibility mode"
MADXCompatibilityError,
match="TFS-Headers can not contain boolean values in MAD-X compatibility mode",
):
validate(df, compatibility=validation_mode)

@pytest.mark.parametrize("validation_mode", ["madx", "mad-x", "mAd-X"])
def test_madx_validation_raises_on_complex_headers(self, _tfs_complex_file, validation_mode):
df = read_tfs(_tfs_complex_file)
with pytest.raises(
MADXCompatibilityError, match="TFS-Headers can not contain complex values in MAD-X compatibility mode"
MADXCompatibilityError,
match="TFS-Headers can not contain complex values in MAD-X compatibility mode",
):
validate(df, compatibility=validation_mode)

Expand All @@ -148,7 +154,8 @@ def test_madx_validation_raises_on_none_headers(self, _tfs_dataframe, validation
df = _tfs_dataframe
df.headers["NONEVALUE"] = None
with pytest.raises(
MADXCompatibilityError, match="TFS-Headers can not contain 'None' values in MAD-X compatibility mode"
MADXCompatibilityError,
match="TFS-Headers can not contain 'None' values in MAD-X compatibility mode",
):
validate(df, compatibility=validation_mode)

Expand Down
Loading