diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e4d8a66..462f6f9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,17 +10,20 @@ concurrency: group: ${{ github.ref }}-${{ github.head_ref }}-pals-python cancel-in-progress: true +permissions: + contents: read # access to check out code and install dependencies + jobs: tests: name: tests runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install diff --git a/pyproject.toml b/pyproject.toml index 1c15359..f5f1ba4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dependencies = [ "pyyaml", "toml", ] -requires-python = ">=3.10" +requires-python = ">=3.11" authors = [ { name="Axel Huebl", email="axelhuebl@lbl.gov" }, { name="Edoardo Zoni", email="ezoni@lbl.gov" }, diff --git a/src/pals/PALS.py b/src/pals/PALS.py index 50d90bd..c948b73 100644 --- a/src/pals/PALS.py +++ b/src/pals/PALS.py @@ -1,20 +1,20 @@ from pydantic import BaseModel from pydantic import model_validator -from typing import List, Optional +from typing import Self from .kinds import Lattice from .kinds.all_elements import get_all_elements_as_annotation from .functions import load_file_to_dict, store_dict_to_file -Facility = List[get_all_elements_as_annotation()] +Facility = list[get_all_elements_as_annotation()] class PALSroot(BaseModel): """Represent the roo PALS structure""" - version: Optional[str] = None + version: str | None = None facility: Facility @@ -37,7 +37,7 @@ def model_dump(self, *args, **kwargs): return data @staticmethod - def from_file(filename: str) -> "PALSroot": + def from_file(filename: str) -> Self: """Load a facility from a text file""" pals_dict = load_file_to_dict(filename) return PALSroot(**pals_dict) diff --git a/src/pals/kinds/BeamBeam.py b/src/pals/kinds/BeamBeam.py index ad4ee6d..ee71e1a 100644 --- a/src/pals/kinds/BeamBeam.py +++ b/src/pals/kinds/BeamBeam.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import BaseElement from ..parameters import BeamBeamParameters @@ -13,4 +13,4 @@ class BeamBeam(BaseElement): kind: Literal["BeamBeam"] = "BeamBeam" # Beam-beam-specific parameters - BeamBeamP: Optional[BeamBeamParameters] = None + BeamBeamP: BeamBeamParameters | None = None diff --git a/src/pals/kinds/BeamLine.py b/src/pals/kinds/BeamLine.py index f7dd881..731207e 100644 --- a/src/pals/kinds/BeamLine.py +++ b/src/pals/kinds/BeamLine.py @@ -1,5 +1,5 @@ from pydantic import model_validator -from typing import List, Literal +from typing import Literal, Self from .all_elements import get_all_elements_as_annotation from .mixin import BaseElement @@ -11,7 +11,7 @@ class BeamLine(BaseElement): kind: Literal["BeamLine"] = "BeamLine" - line: List[get_all_elements_as_annotation()] + line: list[get_all_elements_as_annotation()] @model_validator(mode="before") @classmethod @@ -28,7 +28,7 @@ def model_dump(self, *args, **kwargs): return dump_element_list(self, "line", *args, **kwargs) @staticmethod - def from_file(filename: str) -> "BeamLine": + def from_file(filename: str) -> Self: """Load a BeamLine from a text file""" pals_dict = load_file_to_dict(filename) return BeamLine(**pals_dict) diff --git a/src/pals/kinds/Converter.py b/src/pals/kinds/Converter.py index 14dcccd..78aa93d 100644 --- a/src/pals/kinds/Converter.py +++ b/src/pals/kinds/Converter.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import BaseElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class Converter(BaseElement): kind: Literal["Converter"] = "Converter" # Converter-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/CrabCavity.py b/src/pals/kinds/CrabCavity.py index 8b9e3bc..6c53dc9 100644 --- a/src/pals/kinds/CrabCavity.py +++ b/src/pals/kinds/CrabCavity.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class CrabCavity(ThickElement): kind: Literal["CrabCavity"] = "CrabCavity" # CrabCavity-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/EGun.py b/src/pals/kinds/EGun.py index 172b22a..d728a5c 100644 --- a/src/pals/kinds/EGun.py +++ b/src/pals/kinds/EGun.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class EGun(ThickElement): kind: Literal["EGun"] = "EGun" # EGun-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/FloorShift.py b/src/pals/kinds/FloorShift.py index 33098a9..589c9c6 100644 --- a/src/pals/kinds/FloorShift.py +++ b/src/pals/kinds/FloorShift.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import BaseElement from ..parameters import FloorShiftParameters @@ -11,4 +11,4 @@ class FloorShift(BaseElement): kind: Literal["FloorShift"] = "FloorShift" # Floor shift-specific parameters - FloorShiftP: Optional[FloorShiftParameters] = None + FloorShiftP: FloorShiftParameters | None = None diff --git a/src/pals/kinds/Fork.py b/src/pals/kinds/Fork.py index 4828619..fa7bb97 100644 --- a/src/pals/kinds/Fork.py +++ b/src/pals/kinds/Fork.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import BaseElement from ..parameters import ForkParameters @@ -13,4 +13,4 @@ class Fork(BaseElement): kind: Literal["Fork"] = "Fork" # Fork-specific parameters - ForkP: Optional[ForkParameters] = None + ForkP: ForkParameters | None = None diff --git a/src/pals/kinds/Instrument.py b/src/pals/kinds/Instrument.py index ed6d8e6..4d3f1f4 100644 --- a/src/pals/kinds/Instrument.py +++ b/src/pals/kinds/Instrument.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class Instrument(ThickElement): kind: Literal["Instrument"] = "Instrument" # Instrument-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/Kicker.py b/src/pals/kinds/Kicker.py index 521eb94..aadfb47 100644 --- a/src/pals/kinds/Kicker.py +++ b/src/pals/kinds/Kicker.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class Kicker(ThickElement): kind: Literal["Kicker"] = "Kicker" # Kicker-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/Lattice.py b/src/pals/kinds/Lattice.py index c238e5b..5610e8e 100644 --- a/src/pals/kinds/Lattice.py +++ b/src/pals/kinds/Lattice.py @@ -1,5 +1,5 @@ from pydantic import model_validator -from typing import List, Literal, Union +from typing import Literal, Self from .BeamLine import BeamLine from .PlaceholderName import PlaceholderName @@ -12,7 +12,7 @@ class Lattice(BaseElement): kind: Literal["Lattice"] = "Lattice" - branches: List[Union[BeamLine, PlaceholderName]] + branches: list[BeamLine | PlaceholderName] @model_validator(mode="before") @classmethod @@ -29,7 +29,7 @@ def model_dump(self, *args, **kwargs): return dump_element_list(self, "branches", *args, **kwargs) @staticmethod - def from_file(filename: str) -> "Lattice": + def from_file(filename: str) -> Self: """Load a Lattice from a text file""" pals_dict = load_file_to_dict(filename) return Lattice(**pals_dict) diff --git a/src/pals/kinds/Mask.py b/src/pals/kinds/Mask.py index 69351b0..bca82eb 100644 --- a/src/pals/kinds/Mask.py +++ b/src/pals/kinds/Mask.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class Mask(ThickElement): kind: Literal["Mask"] = "Mask" # Mask-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/Multipole.py b/src/pals/kinds/Multipole.py index 0176a5c..9c2f6fa 100644 --- a/src/pals/kinds/Multipole.py +++ b/src/pals/kinds/Multipole.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class Multipole(ThickElement): kind: Literal["Multipole"] = "Multipole" # Multipole-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/Octupole.py b/src/pals/kinds/Octupole.py index 7f41f2e..ef1685d 100644 --- a/src/pals/kinds/Octupole.py +++ b/src/pals/kinds/Octupole.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class Octupole(ThickElement): kind: Literal["Octupole"] = "Octupole" # Octupole-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/Patch.py b/src/pals/kinds/Patch.py index 85df20a..e6c0c3d 100644 --- a/src/pals/kinds/Patch.py +++ b/src/pals/kinds/Patch.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import PatchParameters @@ -13,4 +13,4 @@ class Patch(ThickElement): kind: Literal["Patch"] = "Patch" # Patch-specific parameters - PatchP: Optional[PatchParameters] = None + PatchP: PatchParameters | None = None diff --git a/src/pals/kinds/Quadrupole.py b/src/pals/kinds/Quadrupole.py index d5da87c..dd51b07 100644 --- a/src/pals/kinds/Quadrupole.py +++ b/src/pals/kinds/Quadrupole.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal, Self from pydantic import model_validator @@ -13,11 +13,11 @@ class Quadrupole(ThickElement): kind: Literal["Quadrupole"] = "Quadrupole" # Quadrupole-specific parameters - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None @model_validator(mode="after") - def validate_at_least_one_multipole(self) -> "Quadrupole": + def validate_at_least_one_multipole(self) -> Self: """Ensure at least one multipole parameter is specified.""" if self.MagneticMultipoleP is None and self.ElectricMultipoleP is None: raise ValueError( diff --git a/src/pals/kinds/RBend.py b/src/pals/kinds/RBend.py index 8bccd78..60b5011 100644 --- a/src/pals/kinds/RBend.py +++ b/src/pals/kinds/RBend.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ( @@ -15,7 +15,7 @@ class RBend(ThickElement): kind: Literal["RBend"] = "RBend" # Bend-specific parameters - BendP: Optional[BendParameters] = None + BendP: BendParameters | None = None - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/RFCavity.py b/src/pals/kinds/RFCavity.py index fd4bf18..036c199 100644 --- a/src/pals/kinds/RFCavity.py +++ b/src/pals/kinds/RFCavity.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ( @@ -18,8 +18,8 @@ class RFCavity(ThickElement): kind: Literal["RFCavity"] = "RFCavity" # RF-specific parameters - RFP: Optional[RFParameters] = None - SolenoidP: Optional[SolenoidParameters] = None + RFP: RFParameters | None = None + SolenoidP: SolenoidParameters | None = None - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/SBend.py b/src/pals/kinds/SBend.py index 96dbf44..da68191 100644 --- a/src/pals/kinds/SBend.py +++ b/src/pals/kinds/SBend.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ( @@ -15,7 +15,7 @@ class SBend(ThickElement): kind: Literal["SBend"] = "SBend" # Bend-specific parameters - BendP: Optional[BendParameters] = None + BendP: BendParameters | None = None - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/Sextupole.py b/src/pals/kinds/Sextupole.py index b47848b..37aa7cc 100644 --- a/src/pals/kinds/Sextupole.py +++ b/src/pals/kinds/Sextupole.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class Sextupole(ThickElement): kind: Literal["Sextupole"] = "Sextupole" # Sextupole-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/Solenoid.py b/src/pals/kinds/Solenoid.py index a9e2fc8..b165fa3 100644 --- a/src/pals/kinds/Solenoid.py +++ b/src/pals/kinds/Solenoid.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ( @@ -17,7 +17,7 @@ class Solenoid(ThickElement): kind: Literal["Solenoid"] = "Solenoid" # Solenoid-specific parameters - SolenoidP: Optional[SolenoidParameters] = None + SolenoidP: SolenoidParameters | None = None - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/UnionEle.py b/src/pals/kinds/UnionEle.py index 03b52b9..be99908 100644 --- a/src/pals/kinds/UnionEle.py +++ b/src/pals/kinds/UnionEle.py @@ -1,5 +1,5 @@ from pydantic import model_validator # noqa -from typing import List, Literal +from typing import Literal from .all_elements import get_all_elements_as_annotation from .mixin import BaseElement @@ -12,7 +12,7 @@ class UnionEle(BaseElement): kind: Literal["UnionEle"] = "UnionEle" # Elements in the union - uses the same union type as BeamLine - elements: List[get_all_elements_as_annotation()] = [] + elements: list[get_all_elements_as_annotation()] = [] @model_validator(mode="before") @classmethod diff --git a/src/pals/kinds/Wiggler.py b/src/pals/kinds/Wiggler.py index fa0ce0d..5f151b8 100644 --- a/src/pals/kinds/Wiggler.py +++ b/src/pals/kinds/Wiggler.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing import Literal from .mixin import ThickElement from ..parameters import ElectricMultipoleParameters, MagneticMultipoleParameters @@ -13,5 +13,5 @@ class Wiggler(ThickElement): kind: Literal["Wiggler"] = "Wiggler" # Wiggler-specific parameters - ElectricMultipoleP: Optional[ElectricMultipoleParameters] = None - MagneticMultipoleP: Optional[MagneticMultipoleParameters] = None + ElectricMultipoleP: ElectricMultipoleParameters | None = None + MagneticMultipoleP: MagneticMultipoleParameters | None = None diff --git a/src/pals/kinds/all_elements.py b/src/pals/kinds/all_elements.py index 5c5348f..29acd12 100644 --- a/src/pals/kinds/all_elements.py +++ b/src/pals/kinds/all_elements.py @@ -6,7 +6,6 @@ from typing import Union - from .ACKicker import ACKicker from .BeamBeam import BeamBeam from .BeginningEle import BeginningEle @@ -40,7 +39,7 @@ from .Wiggler import Wiggler -def get_all_element_types(extra_types: tuple = None): +def get_all_element_types(extra_types: tuple | None = None): """Return a tuple of all element types that can be used in BeamLine or UnionEle.""" element_types = ( "Lattice", # Forward reference to handle circular import @@ -82,7 +81,7 @@ def get_all_element_types(extra_types: tuple = None): return element_types -def get_all_elements_as_annotation(extra_types: tuple = None): +def get_all_elements_as_annotation(extra_types: tuple | None = None): """Return the Union type of all allowed elements with their kind as the discriminator field. Note: PlaceholderName is included to support string references to named elements. diff --git a/src/pals/kinds/mixin/BaseElement.py b/src/pals/kinds/mixin/BaseElement.py index 379ca9c..4f07653 100644 --- a/src/pals/kinds/mixin/BaseElement.py +++ b/src/pals/kinds/mixin/BaseElement.py @@ -1,5 +1,5 @@ from pydantic import BaseModel -from typing import Literal, Optional +from typing import Literal from pals.parameters import ( ApertureParameters, @@ -22,13 +22,13 @@ class BaseElement(BaseModel, validate_assignment=True): name: str # Common parameter groups (optional for all elements) - ApertureP: Optional[ApertureParameters] = None - BodyShiftP: Optional[BodyShiftParameters] = None - FloorP: Optional[FloorParameters] = None - MetaP: Optional[MetaParameters] = None - ReferenceP: Optional[ReferenceParameters] = None - ReferenceChangeP: Optional[ReferenceChangeParameters] = None - TrackingP: Optional[TrackingParameters] = None + ApertureP: ApertureParameters | None = None + BodyShiftP: BodyShiftParameters | None = None + FloorP: FloorParameters | None = None + MetaP: MetaParameters | None = None + ReferenceP: ReferenceParameters | None = None + ReferenceChangeP: ReferenceChangeParameters | None = None + TrackingP: TrackingParameters | None = None def model_dump(self, *args, **kwargs): """This makes sure the element name property is moved out and up to a one-key dictionary""" diff --git a/src/pals/kinds/utils/warnings.py b/src/pals/kinds/utils/warnings.py index 056f20d..8af97a0 100644 --- a/src/pals/kinds/utils/warnings.py +++ b/src/pals/kinds/utils/warnings.py @@ -8,7 +8,7 @@ T = TypeVar("T", bound=type) -def under_construction(element_name: str = None): +def under_construction(element_name: str | None = None): """ Compact decorator to mark an element as under construction. diff --git a/src/pals/schema_version.py b/src/pals/schema_version.py index d2b451e..379db7b 100644 --- a/src/pals/schema_version.py +++ b/src/pals/schema_version.py @@ -1,4 +1,2 @@ -from typing import Optional - # PALS schema version - null for now, will be set when version scheme is finalized -PALS_SCHEMA_VERSION: Optional[str] = None +PALS_SCHEMA_VERSION: str | None = None