Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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
1 change: 0 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# sdds documentation build configuration file, created by
# sphinx-quickstart on Tue Feb 6 12:10:18 2018.
Expand Down
53 changes: 53 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,56 @@ homepage = "https://github.com/pylhc/sdds"
repository = "https://github.com/pylhc/sdds"
documentation = "https://pylhc.github.io/sdds/"
changelog = "https://github.com/pylhc/sdds/blob/master/CHANGELOG.md"

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

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

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

line-length = 100
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 = []
1 change: 1 addition & 0 deletions sdds/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Exposes SddsFile, read_sdds and write_sdds directly in sdds namespace."""

from sdds.classes import SddsFile
from sdds.reader import read_sdds
from sdds.writer import write_sdds
Expand Down
76 changes: 50 additions & 26 deletions sdds/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
Implementation are based on documentation at:
https://ops.aps.anl.gov/manuals/SDDStoolkit/SDDStoolkitsu2.html
"""

import logging
import warnings
from collections.abc import Iterator
from dataclasses import dataclass, fields
from typing import Any, ClassVar, Dict, Iterator, List, Optional, Tuple
from typing import Any, ClassVar

LOGGER = logging.getLogger(__name__)

Expand All @@ -33,11 +35,27 @@
"boolean": "i1",
"string": "s",
}
NUMTYPES_SIZES = {"float": 4, "double": 8, "short": 2, "long": 4, "llong": 8, "char": 1, "boolean": 1}
NUMTYPES_CAST = {"float": float, "double": float, "short": int, "long": int, "llong": int, "char": str, "boolean": int}
NUMTYPES_SIZES = {
"float": 4,
"double": 8,
"short": 2,
"long": 4,
"llong": 8,
"char": 1,
"boolean": 1,
}
NUMTYPES_CAST = {
"float": float,
"double": float,
"short": int,
"long": int,
"llong": int,
"char": str,
"boolean": int,
}


def get_dtype_str(type_: str, endianness: str = "big", length: Optional[int] = None):
def get_dtype_str(type_: str, endianness: str = "big", length: int | None = None):
return f"{ENDIAN[endianness]}{length if length is not None else ''}{NUMTYPES[type_]}"


Expand All @@ -62,8 +80,8 @@ class Description:
contents (str): Optional. Formal specification of the type of data stored in a data set.
"""

text: Optional[str] = None
contents: Optional[str] = None
text: str | None = None
contents: str | None = None
TAG: ClassVar[str] = "&description"

def __repr__(self):
Expand Down Expand Up @@ -120,11 +138,11 @@ class Definition:

name: str
type: str
symbol: Optional[str] = None
units: Optional[str] = None
description: Optional[str] = None
format_string: Optional[str] = None
TAG: ClassVar[Optional[str]] = None
symbol: str | None = None
units: str | None = None
description: str | None = None
format_string: str | None = None
TAG: ClassVar[str | None] = None

def __post_init__(self):
# Fix types (probably strings from reading files) by using the type-hints
Expand All @@ -149,7 +167,9 @@ def __post_init__(self):
# all is fine
continue

LOGGER.debug(f"converting {field.name}: " f"{type(value).__name__} -> {hinted_type.__name__}")
LOGGER.debug(
f"converting {field.name}: {type(value).__name__} -> {hinted_type.__name__}"
)
setattr(self, field.name, hinted_type(value))

def get_key_value_string(self) -> str:
Expand All @@ -158,7 +178,9 @@ def get_key_value_string(self) -> str:
Hint: `ClassVars` (like ``TAG``) are ignored in `fields`.
"""
field_values = {field.name: getattr(self, field.name) for field in fields(self)}
return ", ".join([f"{key}={value}" for key, value in field_values.items() if value is not None])
return ", ".join(
[f"{key}={value}" for key, value in field_values.items() if value is not None]
)

def __repr__(self):
return f"<SDDS {self.__class__.__name__} '{self.name}'>"
Expand Down Expand Up @@ -196,7 +218,7 @@ class Parameter(Definition):
"""

TAG: ClassVar[str] = "&parameter"
fixed_value: Optional[str] = None
fixed_value: str | None = None


@dataclass
Expand All @@ -219,9 +241,9 @@ class Array(Definition):
"""

TAG: ClassVar[str] = "&array"
field_length: Optional[int] = None
group_name: Optional[str] = None
dimensions: Optional[int] = None
field_length: int | None = None
group_name: str | None = None
dimensions: int | None = None


@dataclass
Expand Down Expand Up @@ -279,16 +301,16 @@ class SddsFile:
"""

version: str # This should always be "SDDS1"
description: Optional[Description]
definitions: Dict[str, Definition]
values: Dict[str, Any]
description: Description | None
definitions: dict[str, Definition]
values: dict[str, Any]

def __init__(
self,
version: str,
description: Optional[Description],
definitions_list: List[Definition],
values_list: List[Any],
description: Description | None,
definitions_list: list[Definition],
values_list: list[Any],
) -> None:
self.version = version

Expand All @@ -298,12 +320,14 @@ def __init__(

self.description = description
self.definitions = {definition.name: definition for definition in definitions_list}
self.values = {definition.name: value for definition, value in zip(definitions_list, values_list)}
self.values = {
definition.name: value for definition, value in zip(definitions_list, values_list)
}

def __getitem__(self, name: str) -> Tuple[Definition, Any]:
def __getitem__(self, name: str) -> tuple[Definition, Any]:
return self.definitions[name], self.values[name]

def __iter__(self) -> Iterator[Tuple[Definition, Any]]:
def __iter__(self) -> Iterator[tuple[Definition, Any]]:
for def_name in self.definitions:
yield self[def_name]

Expand Down
Loading
Loading