Skip to content
Open
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
8 changes: 4 additions & 4 deletions .ts_pre_commit_config.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
black: true
black: false
check-xml: true
check-yaml: true
clang-format: false
flake8: true
flake8: false
format-xmllint: false
isort: true
isort: false
mypy: true
ruff: false
ruff: true
towncrier: true
1 change: 1 addition & 0 deletions bin/command_test_csc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ For most CSCs running a commander can be quite dangerous
so please pick a script name that is not easily confused
with the script that runs the CSC.
"""

from lsst.ts.salobj import command_test_csc

command_test_csc()
1 change: 1 addition & 0 deletions bin/get_component_info
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

Run with option ``--help`` for more information.
"""

from lsst.ts.salobj import get_component_info

get_component_info()
1 change: 1 addition & 0 deletions bin/run_test_csc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.

"""An example of how to run a CSC"""

from lsst.ts.salobj import run_test_csc

run_test_csc()
1 change: 1 addition & 0 deletions doc/news/OSW-1886.misc.1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for numpy types in SAL messages.
1 change: 1 addition & 0 deletions doc/news/OSW-1886.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Switched from black, flake8 and isort to ruff.
32 changes: 11 additions & 21 deletions measure_read_speed.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
import asyncio

import numpy as np

from lsst.ts import salobj, utils


async def measure_read_speed(
component_name: str, sal_index: int, attr_name: str, num_messages: int
) -> None:
async def measure_read_speed(component_name: str, sal_index: int, attr_name: str, num_messages: int) -> None:
"""Measure read speed and latency for a specified SAL component and topic.

Parameters
Expand All @@ -25,12 +24,11 @@ async def measure_read_speed(
"""
if num_messages < 2:
raise ValueError(f"num_messages={num_messages} must be >= 2")
async with salobj.Domain() as domain, salobj.SalInfo(
domain=domain, name=component_name, index=sal_index
) as salinfo:
topic = salobj.topics.ReadTopic(
salinfo=salinfo, attr_name=attr_name, max_history=0
)
async with (
salobj.Domain() as domain,
salobj.SalInfo(domain=domain, name=component_name, index=sal_index) as salinfo,
):
topic = salobj.topics.ReadTopic(salinfo=salinfo, attr_name=attr_name, max_history=0)

await salinfo.start()
print("Reader is ready")
Expand All @@ -50,9 +48,7 @@ async def measure_read_speed(
latencies[i] = utils.current_tai() - data.private_sndStamp
dt = utils.current_tai() - t0
read_speed = (num_messages - 1) / dt
num_lost = (
1 + (data.private_seqNum - initial_data.private_seqNum) - num_messages
)
num_lost = 1 + (data.private_seqNum - initial_data.private_seqNum) - num_messages
print(f"Read {read_speed:0.0f} samples/second ({num_messages} samples)")
print(
f"Latency mean = {latencies.mean():0.3f}, stdev = {latencies.std():0.3f}, "
Expand All @@ -62,15 +58,9 @@ async def measure_read_speed(


parser = argparse.ArgumentParser("Measure read speed and latency for one SAL topic")
parser.add_argument(
"component_name_index", help="SAL component name[:sal_index], e.g. Test:1"
)
parser.add_argument(
"topic_attr_name", help="Topic attribute name, e.g. evt_summaryState"
)
parser.add_argument(
"-n", "--number", type=int, default=2000, help="Number of messages to read"
)
parser.add_argument("component_name_index", help="SAL component name[:sal_index], e.g. Test:1")
parser.add_argument("topic_attr_name", help="Topic attribute name, e.g. evt_summaryState")
parser.add_argument("-n", "--number", type=int, default=2000, help="Number of messages to read")
args = parser.parse_args()
component_name, sal_index = salobj.name_to_name_index(args.component_name_index)
asyncio.run(
Expand Down
20 changes: 5 additions & 15 deletions measure_write_speed.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ async def measure_write_speed(
bool: lambda: random.choice([False, True]),
int: lambda: random.randint(0, 100),
float: lambda: random.randrange(-1000, 1000),
str: lambda: "".join(
random.choice(string.ascii_letters) for _ in range(256)
),
str: lambda: "".join(random.choice(string.ascii_letters) for _ in range(256)),
}
if isinstance(value, list):
arr_len = len(value)
Expand All @@ -87,25 +85,17 @@ async def measure_write_speed(
await asyncio.sleep(1)


parser = argparse.ArgumentParser(
"Measure the speed of writing messages for one SAL topic"
)
parser.add_argument(
"component_name_index", help="SAL component name[:sal_index], e.g. Test:1"
)
parser.add_argument(
"topic_attr_name", help="Topic attribute name, e.g. evt_summaryState"
)
parser = argparse.ArgumentParser("Measure the speed of writing messages for one SAL topic")
parser.add_argument("component_name_index", help="SAL component name[:sal_index], e.g. Test:1")
parser.add_argument("topic_attr_name", help="Topic attribute name, e.g. evt_summaryState")
parser.add_argument(
"-i",
"--interval",
type=float,
default=0,
help="Interval between each message (seconds)",
)
parser.add_argument(
"-n", "--number", type=int, default=2000, help="Number of messages to write"
)
parser.add_argument("-n", "--number", type=int, default=2000, help="Number of messages to write")
args = parser.parse_args()
component_name, sal_index = salobj.name_to_name_index(args.component_name_index)
asyncio.run(
Expand Down
20 changes: 5 additions & 15 deletions python/lsst/ts/salobj/async_s3_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,7 @@ def make_bucket_name(s3instance: str, s3category: str = "LFA") -> str:
raise ValueError(f"{argname}={arg} invalid")
bucket_name = f"rubinobs-{s3category}-{s3instance}".lower()
if len(bucket_name) > 63:
raise ValueError(
f"Bucket name {bucket_name!r} too long: len={len(bucket_name)} > 63 chars"
)
raise ValueError(f"Bucket name {bucket_name!r} too long: len={len(bucket_name)} > 63 chars")
return bucket_name

@staticmethod
Expand Down Expand Up @@ -295,13 +293,9 @@ async def upload(
"""
loop = asyncio.get_running_loop()
await loop.run_in_executor(None, self._sync_upload, fileobj, key, callback)
return (
f"{self.service_resource.meta.client.meta.endpoint_url}/{self.name}/{key}"
)
return f"{self.service_resource.meta.client.meta.endpoint_url}/{self.name}/{key}"

async def download(
self, key: str, callback: Callable[[int], None] | None = None
) -> io.BytesIO:
async def download(self, key: str, callback: Callable[[int], None] | None = None) -> io.BytesIO:
"""Download a file-like object from the bucket.

Parameters
Expand Down Expand Up @@ -369,16 +363,12 @@ def _sync_upload(
) -> None:
self.bucket.upload_fileobj(Fileobj=fileobj, Key=key, Callback=callback)

def _sync_download(
self, key: str, callback: Callable[[int], None] | None
) -> io.BytesIO:
def _sync_download(self, key: str, callback: Callable[[int], None] | None) -> io.BytesIO:
fileobj = io.BytesIO()
self.bucket.download_fileobj(Key=key, Fileobj=fileobj, Callback=callback)
# Rewind the fileobj so read returns the data.
fileobj.seek(0)
return fileobj

def _sync_size(self, key: str) -> int:
return self.bucket.meta.client.head_object(Bucket=self.name, Key=key)[
"ContentLength"
]
return self.bucket.meta.client.head_object(Bucket=self.name, Key=key)["ContentLength"]
34 changes: 12 additions & 22 deletions python/lsst/ts/salobj/base_config_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import unittest

import yaml

from lsst.ts.xml import type_hints

from .configurable_csc import ConfigurableCsc
Expand Down Expand Up @@ -130,9 +131,9 @@ def get_config_dir(
assert config_package_root.is_dir()

version = schema["title"].split()[-1]
assert version.startswith(
"v"
), f"version={version} from schema title {schema['title']} does not start with 'v'"
assert version.startswith("v"), (
f"version={version} from schema title {schema['title']} does not start with 'v'"
)

config_dir = pathlib.Path(config_package_root) / sal_name / version
assert config_dir.is_dir()
Expand Down Expand Up @@ -167,18 +168,14 @@ def check_config_files(

config_validator = StandardValidator(schema)
schema_version = schema["title"].split()[-1]
assert schema_version.startswith(
"v"
), f"version={schema_version} from schema title {schema['title']} does not start with 'v'"
assert schema_version.startswith("v"), (
f"version={schema_version} from schema title {schema['title']} does not start with 'v'"
)

config_files = list(config_dir.glob("*.yaml"))
if exclude_glob:
files_to_exclude = set(config_dir.glob(exclude_glob))
config_files = [
filename
for filename in config_files
if filename not in files_to_exclude
]
config_files = [filename for filename in config_files if filename not in files_to_exclude]
found_init = False
site_files = []
override_files = [""]
Expand All @@ -195,24 +192,19 @@ def check_config_files(
if not site_files:
site_files = [""]
except Exception as e:
raise AssertionError(
f"Failed on {config_dir=}, {schema_version=}: {e!r}"
) from e
raise AssertionError(f"Failed on {config_dir=}, {schema_version=}: {e!r}") from e

try:
# Check each site and each site with each override file
for site_file, override_file in itertools.product(
site_files, override_files
):
for site_file, override_file in itertools.product(site_files, override_files):
ConfigurableCsc.read_config_files(
config_validator=config_validator,
config_dir=config_dir,
files_to_read=["_init.yaml", site_file, override_file],
)
except Exception as e:
raise AssertionError(
f"Failed on {config_dir=}, {schema_version=}, "
f"{site_file=}, {override_file=}: {e!r}"
f"Failed on {config_dir=}, {schema_version=}, {site_file=}, {override_file=}: {e!r}"
) from e

def check_standard_config_files(
Expand Down Expand Up @@ -260,9 +252,7 @@ def check_standard_config_files(

if config_dir is None:
if config_package_root is None:
raise RuntimeError(
"config_package_root must be specified if config_dir is None."
)
raise RuntimeError("config_package_root must be specified if config_dir is None.")
if sal_name is None:
raise ValueError("sal_name must be specified if config_dir is None")
config_dir = self.get_config_dir(
Expand Down
Loading