Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e640174
Added missing LvSubstation constructor kwargs
charlta Mar 25, 2026
ae10854
Fixed typing
charlta Mar 25, 2026
1540108
Stopped test crashes timing out trying to narrow Hypothesis examples
charlta Mar 25, 2026
e1b2bc7
Added missing exports
charlta Mar 25, 2026
b2dcde9
Added sequence unpacking support for UnresolvedReference
charlta Mar 25, 2026
22ad23b
Fixed error in LvSubstation to LvFeeder reference resolution
charlta Mar 25, 2026
c695f66
Updated `SetDirection` to correctly use the `@singledispatchmethod` r…
charlta Mar 25, 2026
8fcdc75
`EquipmentTreeBuilder.roots` is now a `dict`
charlta Mar 25, 2026
f9d9083
Renamed `cim_creators.py` -> `fill_fields.py`
charlta Mar 25, 2026
926f880
Fixed typing with private validators
charlta Mar 25, 2026
cf9b9ca
Fixed errors in network translator testing of LvFeeder to LvSubstation.
charlta Mar 25, 2026
e505e1a
Fixed imports in set direction after changes
charlta Mar 26, 2026
ad4e9f4
Renamed `PanDemandResponseFunction` constructor argument `appliances`…
charlta Mar 26, 2026
0af4181
Fixed types on all overrides for `PowerSystemResource.asset_info`
charlta Mar 26, 2026
efc1cb8
You can now pass a list of `TransformerEndRatedS` to the `PowerTransf…
charlta Mar 26, 2026
3b82b23
Coding style
charlta Mar 26, 2026
60268ab
Added sequence unpacking support for `ObjectDifference`.
charlta Mar 26, 2026
072a6b2
Fixed the packing and unpacking of gRPC timestamps
charlta Mar 26, 2026
0fdd197
Merged the kwargs from cim class tests into/with those in `fill_field…
charlta Mar 26, 2026
4189ef3
Removed `PyPackageRequirements` inspection suppressions. Just turn it…
charlta Mar 27, 2026
d254aa9
Pulled the individual object creations out of `pb_creators` and made …
charlta Mar 27, 2026
23df180
Updated the `action` type signatures for `TestNetworkBuilder` to remo…
charlta Mar 27, 2026
db8b2a2
Updated all `Callable` type signatures for callables with unused retu…
charlta Mar 27, 2026
28f9da0
Fixed exception catching and types for grpc interaction tests.
charlta Mar 27, 2026
c03670e
Fixed failing test that was hidden by incorrect exception swallowing.
charlta Mar 27, 2026
ee43ae6
Added a comment on why we disable the shrink phase.
charlta Mar 27, 2026
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
16 changes: 16 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ to prevent the test from timing out while you step through the code:
* Add new classes to corresponding service translator test. [```test/services/.../translator```](test/services)
* Add the required creators to:
- [```pb_creators.py```](test/streaming/get/pb_creators.py)
- [```cim_creators.py```](test/cim/cim_creators.py)
- [```fill_fields.py```](test/cim/fill_fields.py)
- If a relationship involving a branch class has been introduced. Add an entry in SAMPLE SET to ensure the correct leaf class is created for testing.
* Add test for each new comparator to [test/services/...](test/services) package.
* [network](test/services/network/test_network_service_comparator.py)
Expand Down
32 changes: 26 additions & 6 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
# Zepben Python SDK
## [1.3.0] - UNRELEASED
### Breaking Changes
* None.
* Updated `SetDirection` to correctly use the `@singledispatchmethod` registration `run` instead of the old `run_terminal`. Simply replace your `run_terminal`
call with `run`.
* `EquipmentTreeBuilder.roots` is now a `dict` keyed by the `start_item` rather than a `Generator`. This allows better lookup of root items when you are looking
for an explicit items tree.
* Renamed `PanDemandResponseFunction` constructor argument `appliances` -> `appliance` to match the class property.
* Deprecated the following side-hustle shadowy names for `PowerSystemResource.asset_info`, just use `asset_info` directly:
* `Conductor.wire_info`
* `CurrentTransformer.current_transformer_info`
* `PotentialTransformer.potential_transformer_info`
* `PowerTransformer.power_transformer_info`
* `ProtectionRelayFunction.relay_info`
* `ShuntCompensator.shunt_compensator_info`
* `Switch.switch_info`

### New Features
* None.

### Enhancements
* None.
* Added sequence unpacking support for `UnresolvedReference` and `ObjectDifference`.
* `SetDirection.run` now supports `ConductingEquipment`.
* Fixed types on all overrides for `PowerSystemResource.asset_info`, removing the need to shadow them with type specific variants.
* You can now pass a list of `TransformerEndRatedS` to the `PowerTransformerEnd` constructor via the `ratings` argument.
* Updated all `Callable` type signatures for callables with unused return values to accept `Any` instead of `None`. The return is still unused, but requiring
`None` raises types errors if anything is actually returned.

### Fixes
* None.
* Fixed the packing and unpacking of timestamps for `Agreement.validity_interval` in gRPC messages. Fix also ensures all other timestamps correctly support
`None` when optional.

### Notes
* None.
Expand All @@ -23,7 +41,8 @@
* `MeasurementValue.time_stamp`
* `RelayInfo.curve_setting`
* `RelayInfo.reclose_fast`
* Removed `TracedPhases`. `Terminal.normalPhases` and `Terminal.currentPhases` should be used instead of `Terminal.tracedPhases` going forward. (missed in 0.48.0)
* Removed `TracedPhases`. `Terminal.normalPhases` and `Terminal.currentPhases` should be used instead of `Terminal.tracedPhases` going forward. (missed in
0.48.0)

### New Features
* Added the following new CIM classes:
Expand Down Expand Up @@ -58,15 +77,16 @@
* Added `AcLineSegment.wire_info_for_phase(phase: SinglePhaseKind)` to retrieve the `WireInfo` associated with a given phase of a conductor.

### Enhancements
* * `BaseService.contains` has been been expanded to support objects in addition to mRIDs.
*
* `BaseService.contains` has been been expanded to support objects in addition to mRIDs.
* `Agreement` now supports `validity_interval`, the date and time interval the agreement is valid (from going into effect to termination).
* `StreetDetail` now supports extension `building_number`, the number of the building.
* `TownDetail` now supports `country`, the name of the country.
* `ngen()` now directly accepts `dict()`s and will return a generator of the `values()` or `None` if `collection is None`

### Fixes
* Reordered the feeder equipment and direction assignment on database read to prevent parallel feeders from tracing back into the zone substation.
* `NetworkDatabaseTables`, `CustomerDatabaseTables`, `DiagramDatabaseTables` and `BaseEntryWriter` can now be imported from `zepben.ewb` and are officially
* `NetworkDatabaseTables`, `CustomerDatabaseTables`, `DiagramDatabaseTables` and `BaseEntryWriter` can now be imported from `zepben.ewb` and are officially
regarded as public.

### Notes
Expand Down
4 changes: 2 additions & 2 deletions src/zepben/ewb/database/paths/local_ewb_data_file_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from datetime import date
from pathlib import Path
from typing import Callable, Generator, Union
from typing import Callable, Generator, Union, Any

from zepben.ewb import require
from zepben.ewb.database.paths.ewb_data_file_paths import EwbDataFilePaths
Expand All @@ -20,7 +20,7 @@ def __init__(
self,
base_dir: Union[Path, str],
create_path: bool = False,
create_directories_func: Callable[[Path], None] = lambda it: it.mkdir(parents=True),
create_directories_func: Callable[[Path], Any] = lambda it: it.mkdir(parents=True),
is_directory: Callable[[Path], bool] = Path.is_dir,
exists: Callable[[Path], bool] = Path.exists,
list_files: Callable[[Path], Generator[Path, None, None]] = Path.iterdir,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import logging
from abc import ABC, abstractmethod
from typing import Callable, TypeVar, Iterable
from typing import Callable, TypeVar, Iterable, Any

from zepben.ewb.database.sqlite.extensions.prepared_statement import SqlException

Expand Down Expand Up @@ -35,7 +35,7 @@ def save(self) -> bool:
"""
pass

def _save_each(self, items: Iterable[T], saver: Callable[[T], bool], on_save_failure: Callable[[T, Exception], None]) -> bool:
def _save_each(self, items: Iterable[T], saver: Callable[[T], bool], on_save_failure: Callable[[T, Exception], Any]) -> bool:
"""
Save each of the [items] to the database.

Expand All @@ -56,7 +56,7 @@ def _save_each(self, items: Iterable[T], saver: Callable[[T], bool], on_save_fai
return status

@staticmethod
def _validate_save(it: T, saver: [[T], bool], on_save_failure: [[Exception], None]) -> bool:
def _validate_save(it: T, saver: Callable[[T], bool], on_save_failure: Callable[[Exception], Any]) -> bool:
"""
Validate that a save actually works, and convert all exceptions into failures with a callback.

Expand Down
4 changes: 2 additions & 2 deletions src/zepben/ewb/database/sqlite/common/base_database_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from abc import ABC
from sqlite3 import Connection, Cursor, ProgrammingError
from typing import Dict, TypeVar, Type, Generator, Callable, Optional
from typing import Dict, TypeVar, Type, Generator, Callable, Optional, Any

from zepben.ewb.database.sqlite.extensions.prepared_statement import PreparedStatement
from zepben.ewb.database.sqlite.tables.exceptions import MissingTableConfigException
Expand Down Expand Up @@ -90,7 +90,7 @@ def get_insert(self, type_: Type[TSqliteTable]) -> PreparedStatement:
except TypeError:
raise MissingTableConfigException("INTERNAL ERROR: Statements have not been prepared. You must call `prepare_insert_statements` first.")

def for_each_table(self, action: Callable[[SqliteTable], None]):
def for_each_table(self, action: Callable[[SqliteTable], Any]):
"""
Call the `action` on each table.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def add_ratio(self, numerator_index: int, denominator_index: int, value: Optiona
self.add_value(numerator_index, value.numerator)
self.add_value(denominator_index, value.denominator)

def try_execute_single_update(self, on_error: Optional[Callable[[Exception], None]] = None) -> bool:
def try_execute_single_update(self, on_error: Optional[Callable[[Exception], Any]] = None) -> bool:
"""
Execute an update on the database with the given `query`.
Failures will be logged as warnings.
Expand Down
10 changes: 5 additions & 5 deletions src/zepben/ewb/database/sqlite/network/network_cim_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ def save_current_transformer(self, current_transformer: CurrentTransformer) -> b
table = self._database_tables.get_table(TableCurrentTransformers)
insert = self._database_tables.get_insert(TableCurrentTransformers)

insert.add_value(table.current_transformer_info_mrid.query_index, self._mrid_or_none(current_transformer.current_transformer_info))
insert.add_value(table.current_transformer_info_mrid.query_index, self._mrid_or_none(current_transformer.asset_info))
insert.add_value(table.core_burden.query_index, current_transformer.core_burden)

return self._save_sensor(table, insert, current_transformer, "current transformer")
Expand Down Expand Up @@ -1280,7 +1280,7 @@ def save_potential_transformer(self, potential_transformer: PotentialTransformer
table = self._database_tables.get_table(TablePotentialTransformers)
insert = self._database_tables.get_insert(TablePotentialTransformers)

insert.add_value(table.potential_transformer_info_mrid.query_index, self._mrid_or_none(potential_transformer.potential_transformer_info))
insert.add_value(table.potential_transformer_info_mrid.query_index, self._mrid_or_none(potential_transformer.asset_info))
insert.add_value(table.type.query_index, potential_transformer.type.short_name)

return self._save_sensor(table, insert, potential_transformer, "potential transformer")
Expand Down Expand Up @@ -1805,7 +1805,7 @@ def _save_conductor(self, table: TableConductors, insert: PreparedStatement, con
insert.add_value(table.length.query_index, conductor.length)
insert.add_value(table.design_temperature.query_index, conductor.design_temperature)
insert.add_value(table.design_rating.query_index, conductor.design_rating)
insert.add_value(table.wire_info_mrid.query_index, self._mrid_or_none(conductor.wire_info))
insert.add_value(table.wire_info_mrid.query_index, self._mrid_or_none(conductor.asset_info))

return self._save_conducting_equipment(table, insert, conductor, description)

Expand Down Expand Up @@ -2243,7 +2243,7 @@ def save_power_transformer(self, power_transformer: PowerTransformer) -> bool:
insert.add_value(table.transformer_utilisation.query_index, power_transformer.transformer_utilisation)
insert.add_value(table.construction_kind.query_index, power_transformer.construction_kind.short_name)
insert.add_value(table.function.query_index, power_transformer.function.short_name)
insert.add_value(table.power_transformer_info_mrid.query_index, self._mrid_or_none(power_transformer.power_transformer_info))
insert.add_value(table.power_transformer_info_mrid.query_index, self._mrid_or_none(power_transformer.asset_info))

return self._save_conducting_equipment(table, insert, power_transformer, "power transformer")

Expand Down Expand Up @@ -2440,7 +2440,7 @@ def _save_switch(self, table: TableSwitches, insert: PreparedStatement, switch:
# noinspection PyProtectedMember
insert.add_value(table.open.query_index, switch._open)
insert.add_value(table.rated_current.query_index, switch.rated_current)
insert.add_value(table.switch_info_mrid.query_index, self._mrid_or_none(switch.switch_info))
insert.add_value(table.switch_info_mrid.query_index, self._mrid_or_none(switch.asset_info))

return self._save_conducting_equipment(table, insert, switch, description)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

__all__ = ["RelayInfo"]

from typing import Optional, List, Generator, Callable
from typing import Optional, List, Generator, Callable, Any

from zepben.ewb.model.cim.extensions.zbex import zbex
from zepben.ewb.model.cim.iec61968.assets.asset_info import AssetInfo
Expand Down Expand Up @@ -57,7 +57,7 @@ def get_delay(self, index: int) -> float:
else:
raise IndexError(index)

def for_each_delay(self, action: Callable[[int, float], None]):
def for_each_delay(self, action: Callable[[int, float], Any]):
"""
Call the `action` on each delay in the `reclose_delays` collection

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ class PanDemandResponseFunction(EndDeviceFunction):

_appliance_bitmask: Optional[int] = None

def __init__(self, appliances: Union[int, ControlledAppliance] = None, **kwargs):
def __init__(self, appliance: Union[int, ControlledAppliance] = None, **kwargs):
super(PanDemandResponseFunction, self).__init__(**kwargs)
if appliances is not None:
self.appliance = appliances
if appliance is not None:
self.appliance = appliance

@property
def appliance(self) -> Optional[ControlledAppliance]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ class LvSubstation(EquipmentContainer):
_current_energizing_feeders_by_id: dict[str | None, 'Feeder'] | None = None
_normal_energized_lv_feeders_by_id: dict[str | None, LvFeeder] | None = None

def __init__(
self,
normal_energizing_feeders: list['Feeder'] | None = None,
current_energizing_feeders: list['Feeder'] | None = None,
normal_energized_lv_feeders: list[LvFeeder] | None = None,
**kwargs
):
super(LvSubstation, self).__init__(**kwargs)
if normal_energizing_feeders:
for lv_feeder in normal_energizing_feeders:
self.add_normal_energizing_feeder(lv_feeder)
if current_energizing_feeders:
for lv_feeder in current_energizing_feeders:
self.add_current_energizing_feeder(lv_feeder)
if normal_energized_lv_feeders:
for lv_feeder in normal_energized_lv_feeders:
self.add_normal_energized_lv_feeder(lv_feeder)

@zbex
@property
def normal_energizing_feeders(self) -> Generator["Feeder", None, None]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@

__all__ = ["ProtectionRelayFunction"]

from typing import Optional, List, Generator, Iterable, Callable, TYPE_CHECKING
import sys
import warnings
from typing import Optional, List, Generator, Iterable, Callable, TYPE_CHECKING, Any
if sys.version_info >= (3, 13):
from warnings import deprecated
else:
from typing_extensions import deprecated

from zepben.ewb.model.cim.extensions.iec61970.base.protection.power_direction_kind import PowerDirectionKind
from zepben.ewb.model.cim.extensions.iec61970.base.protection.protection_kind import ProtectionKind
Expand All @@ -30,6 +36,8 @@ class ProtectionRelayFunction(PowerSystemResource):
A function that a relay implements to protect equipment.
"""

asset_info: Optional[RelayInfo] = None

model: Optional[str] = None
"""[ZBEX] The protection equipment type name(manufacturer information)."""

Expand Down Expand Up @@ -58,13 +66,16 @@ class ProtectionRelayFunction(PowerSystemResource):

_thresholds: Optional[List[RelaySetting]] = None

def __init__(self,
sensors: Iterable[Sensor] = None,
protected_switches: Iterable[ProtectedSwitch] = None,
schemes: Iterable[ProtectionRelayScheme] = None,
time_limits: Iterable[float] = None,
thresholds: Iterable[RelaySetting] = None,
relay_info: RelayInfo = None, **kwargs):
def __init__(
self,
sensors: Iterable[Sensor] = None,
protected_switches: Iterable[ProtectedSwitch] = None,
schemes: Iterable[ProtectionRelayScheme] = None,
time_limits: Iterable[float] = None,
thresholds: Iterable[RelaySetting] = None,
relay_info: RelayInfo | None = None,
**kwargs
):
super(ProtectionRelayFunction, self).__init__(**kwargs)

if sensors is not None:
Expand All @@ -83,14 +94,17 @@ def __init__(self,
for threshold in thresholds:
self.add_threshold(threshold)
if relay_info is not None:
self.relay_info = relay_info
warnings.warn("relay_info is deprecated, use asset_info instead.")
self.asset_info = relay_info

@property
@deprecated("use asset_info instead.")
def relay_info(self):
"""Datasheet information for this CurrentRelay"""
return self.asset_info

@relay_info.setter
@deprecated("use asset_info instead.")
def relay_info(self, relay_info: Optional[RelayInfo]):
self.asset_info = relay_info

Expand All @@ -103,7 +117,7 @@ def thresholds(self) -> Generator[RelaySetting, None, None]:
"""
return ngen(self._thresholds)

def for_each_threshold(self, action: Callable[[int, RelaySetting], None]):
def for_each_threshold(self, action: Callable[[int, RelaySetting], Any]):
"""
Call the `action` on each :class:`RelaySetting` in the `thresholds` collection

Expand Down Expand Up @@ -191,7 +205,7 @@ def time_limits(self) -> Generator[float, None, None]:
"""
return ngen(self._time_limits)

def for_each_time_limit(self, action: Callable[[int, float], None]):
def for_each_time_limit(self, action: Callable[[int, float], Any]):
"""
Call the `action` on each time limit in the `time_limits` collection

Expand Down
4 changes: 2 additions & 2 deletions src/zepben/ewb/model/cim/iec61968/common/location.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

__all__ = ["Location"]

from typing import List, Optional, Generator, Callable
from typing import List, Optional, Generator, Callable, Any

from zepben.ewb.model.cim.iec61968.common.position_point import PositionPoint
from zepben.ewb.model.cim.iec61968.common.street_address import StreetAddress
Expand Down Expand Up @@ -61,7 +61,7 @@ def get_point(self, sequence_number: int) -> PositionPoint:
def __getitem__(self, item):
return self.get_point(item)

def for_each_point(self, action: Callable[[int, PositionPoint], None]):
def for_each_point(self, action: Callable[[int, PositionPoint], Any]):
"""
Call the `action` on each :class:`PositionPoint` in the `points` collection

Expand Down
Loading
Loading