From 8dbdaf5a52bca430a03c6f934b918666b4a86382 Mon Sep 17 00:00:00 2001 From: Gary Yendell Date: Thu, 4 Sep 2025 09:55:25 +0000 Subject: [PATCH] Adopt copier 4.2.0 --- .copier-answers.yml | 3 +- .devcontainer/devcontainer.json | 2 +- .github/CONTRIBUTING.md | 2 +- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .../actions/install_requirements/action.yml | 3 +- .github/dependabot.yml | 4 +++ .github/workflows/_check.yml | 27 ----------------- .github/workflows/_docs.yml | 1 + .github/workflows/_release.yml | 2 +- .github/workflows/_test.yml | 2 +- .github/workflows/ci.yml | 21 +++++-------- .pre-commit-config.yaml | 2 +- pyproject.toml | 2 ++ src/fastcs/backend.py | 6 ++-- src/fastcs/cs_methods.py | 18 +++++------ src/fastcs/exceptions.py | 4 +-- src/fastcs/launch.py | 4 +-- src/fastcs/transport/__init__.py | 4 +-- src/fastcs/transport/epics/ca/util.py | 4 +-- src/fastcs/transport/epics/gui.py | 10 +++---- .../{graphQL => graphql}/__init__.py | 0 .../transport/{graphQL => graphql}/adapter.py | 2 +- .../graphQL.py => graphql/graphql.py} | 4 +-- .../transport/{graphQL => graphql}/options.py | 0 src/fastcs/wrappers.py | 4 +-- tests/conftest.py | 30 +++++++++---------- tests/test_backend.py | 4 +-- tests/test_cs_methods.py | 18 +++++------ tests/test_docs_snippets.py | 4 +-- tests/transport/epics/ca/test_softioc.py | 12 ++++---- tests/transport/epics/pva/test_p4p.py | 6 ++-- .../{test_graphQL.py => test_graphql.py} | 2 +- 32 files changed, 93 insertions(+), 116 deletions(-) delete mode 100644 .github/workflows/_check.yml rename src/fastcs/transport/{graphQL => graphql}/__init__.py (100%) rename src/fastcs/transport/{graphQL => graphql}/adapter.py (95%) rename src/fastcs/transport/{graphQL/graphQL.py => graphql/graphql.py} (98%) rename src/fastcs/transport/{graphQL => graphql}/options.py (100%) rename tests/transport/graphQL/{test_graphQL.py => test_graphql.py} (99%) diff --git a/.copier-answers.yml b/.copier-answers.yml index 2dbcef665..cf365f6d3 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 2.6.0 +_commit: 4.2.0 _src_path: gh:DiamondLightSource/python-copier-template author_email: gary.yendell@diamond.ac.uk author_name: Gary Yendell @@ -16,4 +16,5 @@ github_org: DiamondLightSource package_name: fastcs pypi: true repo_name: FastCS +strict_typing: false type_checker: pyright diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 979a89ce3..fce9dd594 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -29,7 +29,7 @@ }, "features": { // add in eternal history and other bash features - "ghcr.io/diamondlightsource/devcontainer-features/bash-config:1.0.0": {} + "ghcr.io/diamondlightsource/devcontainer-features/bash-config:1": {} }, // Create the config folder for the bash-config feature "initializeCommand": "mkdir -p ${localEnv:HOME}/.config/bash-config", diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8e7af809f..932bcdccf 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -24,4 +24,4 @@ It is recommended that developers use a [vscode devcontainer](https://code.visua This project was created using the [Diamond Light Source Copier Template](https://github.com/DiamondLightSource/python-copier-template) for Python projects. -For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.6.0/how-to.html). +For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/4.2.0/how-to.html). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index aa65892f3..92087f5bc 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,7 +7,7 @@ assignees: '' --- -Describe the bug, including a clear and concise description of the expected behavior, the actual behavior and the context in which you encountered it (ideally include details of your environment). +Describe the bug, including a clear and concise description of the expected behaviour, the actual behavior and the context in which you encountered it (ideally include details of your environment). ## Steps To Reproduce Steps to reproduce the behavior: diff --git a/.github/actions/install_requirements/action.yml b/.github/actions/install_requirements/action.yml index d33e08052..7a7bda0d4 100644 --- a/.github/actions/install_requirements/action.yml +++ b/.github/actions/install_requirements/action.yml @@ -15,7 +15,8 @@ runs: run: | PYTHON_VERSION="${{ inputs.python-version }}" if [ $PYTHON_VERSION == "dev" ]; then - PYTHON_VERSION=$(sed -n "s/ARG PYTHON_VERSION=//p" Dockerfile) + # python version from Dockerfile, removing potential pinned sha + PYTHON_VERSION=$(sed -Ene "s/ARG PYTHON_VERSION=([0-9\.]+).*/\1/p" Dockerfile) fi echo "PYTHON_VERSION=$PYTHON_VERSION" >> "$GITHUB_ENV" shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 184ba3631..d2c2a0d6f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,6 +13,8 @@ updates: actions: patterns: - "*" + commit-message: + prefix: "chore" - package-ecosystem: "pip" directory: "/" @@ -22,3 +24,5 @@ updates: dev-dependencies: patterns: - "*" + commit-message: + prefix: "chore" diff --git a/.github/workflows/_check.yml b/.github/workflows/_check.yml deleted file mode 100644 index a6139c19f..000000000 --- a/.github/workflows/_check.yml +++ /dev/null @@ -1,27 +0,0 @@ -on: - workflow_call: - outputs: - branch-pr: - description: The PR number if the branch is in one - value: ${{ jobs.pr.outputs.branch-pr }} - -jobs: - pr: - runs-on: "ubuntu-latest" - outputs: - branch-pr: ${{ steps.script.outputs.result }} - steps: - - uses: actions/github-script@v7 - id: script - if: github.event_name == 'push' - with: - script: | - const prs = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - head: context.repo.owner + ':${{ github.ref_name }}' - }) - if (prs.data.length) { - console.log(`::notice ::Skipping CI on branch push as it is already run in PR #${prs.data[0]["number"]}`) - return prs.data[0]["number"] - } diff --git a/.github/workflows/_docs.yml b/.github/workflows/_docs.yml index a1cafcaed..1f5491bae 100644 --- a/.github/workflows/_docs.yml +++ b/.github/workflows/_docs.yml @@ -1,6 +1,7 @@ on: workflow_call: + jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/_release.yml b/.github/workflows/_release.yml index 81b626438..3f949fcc4 100644 --- a/.github/workflows/_release.yml +++ b/.github/workflows/_release.yml @@ -23,7 +23,7 @@ jobs: - name: Create GitHub Release # We pin to the SHA, not the tag, for security reasons. # https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions - uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 # v2.0.9 + uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }} files: "*" diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index f652d4145..552b29d08 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -54,7 +54,7 @@ jobs: run: tox -e tests - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: name: ${{ inputs.python-version }}/${{ inputs.runs-on }} files: cov.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be681822c..7eea5e33f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,26 +2,24 @@ name: CI on: push: + branches: + - main + tags: + - '*' pull_request: jobs: - check: - uses: ./.github/workflows/_check.yml lint: - needs: check - if: needs.check.outputs.branch-pr == '' uses: ./.github/workflows/_tox.yml with: tox: pre-commit,type-checking test: - needs: check - if: needs.check.outputs.branch-pr == '' strategy: matrix: runs-on: ["ubuntu-latest"] # can add windows-latest, macos-latest - python-version: ["3.11", "3.12"] + python-version: ["3.11", "3.12", "3.13"] include: # Include one that runs in the dev environment - runs-on: "ubuntu-latest" @@ -35,13 +33,10 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} docs: - needs: check - if: needs.check.outputs.branch-pr == '' uses: ./.github/workflows/_docs.yml + dist: - needs: check - if: needs.check.outputs.branch-pr == '' uses: ./.github/workflows/_dist.yml helm: @@ -52,14 +47,14 @@ jobs: pypi: if: github.ref_type == 'tag' - needs: [helm, dist] + needs: [dist, test, helm] uses: ./.github/workflows/_pypi.yml permissions: id-token: write release: if: github.ref_type == 'tag' - needs: [dist, docs, helm] + needs: [dist, test, docs, helm] uses: ./.github/workflows/_release.yml permissions: contents: write diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 80e22ba90..20d01fe42 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: ruff name: lint with ruff language: system - entry: ruff check --force-exclude + entry: ruff check --force-exclude --fix types: [python] require_serial: true diff --git a/pyproject.toml b/pyproject.toml index 9e404b76d..9b8da0098 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] description = "Control system agnostic framework for building Device support in Python that will work for both EPICS and Tango" dependencies = [ @@ -128,6 +129,7 @@ lint.select = [ "C4", # flake8-comprehensions - https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4 "E", # pycodestyle errors - https://docs.astral.sh/ruff/rules/#error-e "F", # pyflakes rules - https://docs.astral.sh/ruff/rules/#pyflakes-f + "N", # pep8-naming - https://docs.astral.sh/ruff/rules/#pep8-naming-n "W", # pycodestyle warnings - https://docs.astral.sh/ruff/rules/#warning-w "I", # isort - https://docs.astral.sh/ruff/rules/#isort-i "UP", # pyupgrade - https://docs.astral.sh/ruff/rules/#pyupgrade-up diff --git a/src/fastcs/backend.py b/src/fastcs/backend.py index 0387ff7b0..2e37fa2b4 100644 --- a/src/fastcs/backend.py +++ b/src/fastcs/backend.py @@ -8,7 +8,7 @@ from .attributes import ONCE, AttrHandlerR, AttrHandlerW, AttrR, AttrW from .controller import BaseController, Controller from .controller_api import ControllerAPI -from .exceptions import FastCSException +from .exceptions import FastCSError from .util import validate_hinted_attributes @@ -63,7 +63,7 @@ def _scan_done(self, task: asyncio.Task): try: task.result() except Exception as e: - raise FastCSException( + raise FastCSError( "Exception raised in scan method of " f"{self._controller.__class__.__name__}" ) from e @@ -86,7 +86,7 @@ def _link_put_tasks(controller_api: ControllerAPI) -> None: case AttrW(): attribute.add_process_callback(method.fn) case _: - raise FastCSException( + raise FastCSError( f"Mode {attribute.access_mode} does not " f"support put operations for {name}" ) diff --git a/src/fastcs/cs_methods.py b/src/fastcs/cs_methods.py index 8f7f216b5..824a2d1da 100644 --- a/src/fastcs/cs_methods.py +++ b/src/fastcs/cs_methods.py @@ -6,7 +6,7 @@ from fastcs.controller import BaseController -from .exceptions import FastCSException +from .exceptions import FastCSError MethodCallback = Callable[..., Coroutine[None, None, None]] """Generic base class for all `Controller` methods""" @@ -48,10 +48,10 @@ def __init__(self, fn: MethodCallback, *, group: str | None = None) -> None: def _validate(self, fn: MethodCallback) -> None: if self.return_type not in (None, Signature.empty): - raise FastCSException("Method return type must be None or empty") + raise FastCSError("Method return type must be None or empty") if not iscoroutinefunction(fn): - raise FastCSException("Method must be async function") + raise FastCSError("Method must be async function") @property def return_type(self): @@ -89,7 +89,7 @@ def _validate(self, fn: CommandCallback) -> None: super()._validate(fn) if not len(self.parameters) == 0: - raise FastCSException(f"Command method cannot have arguments: {fn}") + raise FastCSError(f"Command method cannot have arguments: {fn}") async def __call__(self): return await self._fn() @@ -116,7 +116,7 @@ def _validate(self, fn: ScanCallback) -> None: super()._validate(fn) if not len(self.parameters) == 0: - raise FastCSException("Scan method cannot have arguments") + raise FastCSError("Scan method cannot have arguments") async def __call__(self): return await self._fn() @@ -132,7 +132,7 @@ def _validate(self, fn: PutCallback) -> None: super()._validate(fn) if not len(self.parameters) == 1: - raise FastCSException("Put method can only take one argument") + raise FastCSError("Put method can only take one argument") async def __call__(self, value: Any): return await self._fn(value) @@ -158,7 +158,7 @@ def _validate(self, fn: UnboundCommandCallback[Controller_T]) -> None: super()._validate(fn) if not len(self.parameters) == 1: - raise FastCSException("Command method cannot have arguments") + raise FastCSError("Command method cannot have arguments") def bind(self, controller: Controller_T) -> Command: return Command(MethodType(self.fn, controller), group=self.group) @@ -191,7 +191,7 @@ def _validate(self, fn: UnboundScanCallback[Controller_T]) -> None: super()._validate(fn) if not len(self.parameters) == 1: - raise FastCSException("Scan method cannot have arguments") + raise FastCSError("Scan method cannot have arguments") def bind(self, controller: Controller_T) -> Scan: return Scan(MethodType(self.fn, controller), self._period) @@ -210,7 +210,7 @@ def _validate(self, fn: UnboundPutCallback[Controller_T]) -> None: super()._validate(fn) if not len(self.parameters) == 2: - raise FastCSException("Put method can only take one argument") + raise FastCSError("Put method can only take one argument") def bind(self, controller: Controller_T) -> Put: return Put(MethodType(self.fn, controller)) diff --git a/src/fastcs/exceptions.py b/src/fastcs/exceptions.py index b151c9f98..45fbad3dc 100644 --- a/src/fastcs/exceptions.py +++ b/src/fastcs/exceptions.py @@ -1,8 +1,8 @@ -class FastCSException(Exception): +class FastCSError(Exception): """Base class for general problems in the running of a FastCS transport.""" -class LaunchError(FastCSException): +class LaunchError(FastCSError): """For when there is an error in launching FastCS with the given transports and controller. """ diff --git a/src/fastcs/launch.py b/src/fastcs/launch.py index a96fdaa45..11805672f 100644 --- a/src/fastcs/launch.py +++ b/src/fastcs/launch.py @@ -20,7 +20,7 @@ from .transport.adapter import TransportAdapter from .transport.epics.ca.options import EpicsCAOptions from .transport.epics.pva.options import EpicsPVAOptions -from .transport.graphQL.options import GraphQLOptions +from .transport.graphql.options import GraphQLOptions from .transport.rest.options import RestOptions from .transport.tango.options import TangoOptions @@ -76,7 +76,7 @@ def __init__( option, ) case GraphQLOptions(): - from .transport.graphQL.adapter import GraphQLTransport + from .transport.graphql.adapter import GraphQLTransport transport = GraphQLTransport( self._backend.controller_api, diff --git a/src/fastcs/transport/__init__.py b/src/fastcs/transport/__init__.py index b9c0d097c..ba051fb24 100644 --- a/src/fastcs/transport/__init__.py +++ b/src/fastcs/transport/__init__.py @@ -3,8 +3,8 @@ from .epics.options import EpicsGUIOptions as EpicsGUIOptions from .epics.options import EpicsIOCOptions as EpicsIOCOptions from .epics.pva.options import EpicsPVAOptions as EpicsPVAOptions -from .graphQL.options import GraphQLOptions as GraphQLOptions -from .graphQL.options import GraphQLServerOptions as GraphQLServerOptions +from .graphql.options import GraphQLOptions as GraphQLOptions +from .graphql.options import GraphQLServerOptions as GraphQLServerOptions from .rest.options import RestOptions as RestOptions from .rest.options import RestServerOptions as RestServerOptions from .tango.options import TangoDSROptions as TangoDSROptions diff --git a/src/fastcs/transport/epics/ca/util.py b/src/fastcs/transport/epics/ca/util.py index 9f009b34c..b08f5beea 100644 --- a/src/fastcs/transport/epics/ca/util.py +++ b/src/fastcs/transport/epics/ca/util.py @@ -4,7 +4,7 @@ from fastcs.attributes import Attribute, AttrR, AttrRW, AttrW from fastcs.datatypes import Bool, DataType, Enum, Float, Int, String, T, Waveform -from fastcs.exceptions import FastCSException +from fastcs.exceptions import FastCSError _MBB_FIELD_PREFIXES = ( "ZR", @@ -151,6 +151,6 @@ def builder_callable_from_attribute( case Waveform(): return builder.WaveformIn if make_in_record else builder.WaveformOut case _: - raise FastCSException( + raise FastCSError( f"EPICS unsupported datatype on {attribute}: {attribute.datatype}" ) diff --git a/src/fastcs/transport/epics/gui.py b/src/fastcs/transport/epics/gui.py index 698102b9a..7c355c8eb 100644 --- a/src/fastcs/transport/epics/gui.py +++ b/src/fastcs/transport/epics/gui.py @@ -38,7 +38,7 @@ Table, Waveform, ) -from fastcs.exceptions import FastCSException +from fastcs.exceptions import FastCSError from fastcs.util import numpy_to_fastcs_datatype, snake_to_pascal from .options import EpicsGUIFormat, EpicsGUIOptions @@ -70,7 +70,7 @@ def _get_read_widget(self, fastcs_datatype: DataType) -> ReadWidgetUnion | None: case Waveform(): return None case datatype: - raise FastCSException(f"Unsupported type {type(datatype)}: {datatype}") + raise FastCSError(f"Unsupported type {type(datatype)}: {datatype}") def _get_write_widget(self, fastcs_datatype: DataType) -> WriteWidgetUnion | None: match fastcs_datatype: @@ -85,7 +85,7 @@ def _get_write_widget(self, fastcs_datatype: DataType) -> WriteWidgetUnion | Non case Waveform(): return None case datatype: - raise FastCSException(f"Unsupported type {type(datatype)}: {datatype}") + raise FastCSError(f"Unsupported type {type(datatype)}: {datatype}") def _get_attribute_component( self, attr_path: list[str], name: str, attribute: Attribute @@ -116,7 +116,7 @@ def _get_attribute_component( return None return SignalW(name=name, write_pv=pv, write_widget=write_widget) case _: - raise FastCSException(f"Unsupported attribute type: {type(attribute)}") + raise FastCSError(f"Unsupported attribute type: {type(attribute)}") def _get_command_component(self, attr_path: list[str], name: str): pv = self._get_pv(attr_path, name) @@ -134,7 +134,7 @@ def create_gui(self, options: EpicsGUIOptions | None = None) -> None: options = EpicsGUIOptions() if options.file_format is EpicsGUIFormat.edl: - raise FastCSException("FastCS does not support .edl screens.") + raise FastCSError("FastCS does not support .edl screens.") assert options.output_path.suffix == options.file_format.value options.output_path.parent.mkdir(parents=True, exist_ok=True) diff --git a/src/fastcs/transport/graphQL/__init__.py b/src/fastcs/transport/graphql/__init__.py similarity index 100% rename from src/fastcs/transport/graphQL/__init__.py rename to src/fastcs/transport/graphql/__init__.py diff --git a/src/fastcs/transport/graphQL/adapter.py b/src/fastcs/transport/graphql/adapter.py similarity index 95% rename from src/fastcs/transport/graphQL/adapter.py rename to src/fastcs/transport/graphql/adapter.py index b59deb9d3..e9430589b 100644 --- a/src/fastcs/transport/graphQL/adapter.py +++ b/src/fastcs/transport/graphql/adapter.py @@ -1,7 +1,7 @@ from fastcs.controller_api import ControllerAPI from fastcs.transport.adapter import TransportAdapter -from .graphQL import GraphQLServer +from .graphql import GraphQLServer from .options import GraphQLOptions diff --git a/src/fastcs/transport/graphQL/graphQL.py b/src/fastcs/transport/graphql/graphql.py similarity index 98% rename from src/fastcs/transport/graphQL/graphQL.py rename to src/fastcs/transport/graphql/graphql.py index 69743701c..c62eb7a72 100644 --- a/src/fastcs/transport/graphQL/graphQL.py +++ b/src/fastcs/transport/graphql/graphql.py @@ -9,7 +9,7 @@ from fastcs.attributes import AttrR, AttrRW, AttrW, T from fastcs.controller_api import ControllerAPI -from fastcs.exceptions import FastCSException +from fastcs.exceptions import FastCSError from .options import GraphQLServerOptions @@ -102,7 +102,7 @@ def _process_sub_apis(self, root_controller_api: ControllerAPI): def create_schema(self) -> strawberry.Schema: """Create a Strawberry Schema to load into a GraphQL application.""" if not self.queries: - raise FastCSException( + raise FastCSError( "Can't create GraphQL transport from ControllerAPI with no read " "attributes" ) diff --git a/src/fastcs/transport/graphQL/options.py b/src/fastcs/transport/graphql/options.py similarity index 100% rename from src/fastcs/transport/graphQL/options.py rename to src/fastcs/transport/graphql/options.py diff --git a/src/fastcs/wrappers.py b/src/fastcs/wrappers.py index 4462a559d..1f5903715 100644 --- a/src/fastcs/wrappers.py +++ b/src/fastcs/wrappers.py @@ -9,7 +9,7 @@ UnboundScan, UnboundScanCallback, ) -from .exceptions import FastCSException +from .exceptions import FastCSError def scan( @@ -18,7 +18,7 @@ def scan( """Sets up a scan over the wrapped method.""" if period <= 0: - raise FastCSException("Scan method must have a positive scan period") + raise FastCSError("Scan method must have a positive scan period") def wrapper(fn: UnboundScanCallback[Controller_T]) -> UnboundScan[Controller_T]: return UnboundScan(fn, period) diff --git a/tests/conftest.py b/tests/conftest.py index 80a76711d..c7c982afd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -116,8 +116,8 @@ def write(self, s): # type: ignore def run_ioc_as_subprocess( run_ioc: Callable, ctxt: DefaultContext ) -> Generator[tuple[str, multiprocessing.Queue], None, None]: - IOC_STARTUP_TIMEOUT = 10 - IOC_STARTUP_TIMEOUT_ERROR = TimeoutError("IOC did not start in time") + ioc_startup_timeout = 10 + ioc_startup_timeout_error = TimeoutError("IOC did not start in time") pv_prefix = str(uuid4()) error_queue = ctxt.Queue() @@ -133,14 +133,14 @@ def run_ioc_as_subprocess( while True: try: if "Running FastCS IOC" in ( - stdout_queue.get(timeout=IOC_STARTUP_TIMEOUT) # type: ignore + stdout_queue.get(timeout=ioc_startup_timeout) # type: ignore ): stdout_queue.get() # get the newline break except Exception as error: - raise IOC_STARTUP_TIMEOUT_ERROR from error - if time.monotonic() - start_time > IOC_STARTUP_TIMEOUT: - raise IOC_STARTUP_TIMEOUT_ERROR + raise ioc_startup_timeout_error from error + if time.monotonic() - start_time > ioc_startup_timeout: + raise ioc_startup_timeout_error time.sleep(0.1) yield pv_prefix, stdout_queue @@ -156,7 +156,7 @@ def run_ioc_as_subprocess( error_queue.close() stdout_queue.close() process.terminate() - process.join(timeout=IOC_STARTUP_TIMEOUT) + process.join(timeout=ioc_startup_timeout) @pytest.fixture(scope="module") @@ -186,13 +186,13 @@ def tango_system(): @pytest.fixture(scope="session") def register_device(): - ATTEMPTS = 10 - SLEEP = 1 + attempts = 10 + sleep = 1 if not os.getenv("TANGO_HOST"): raise RuntimeError("TANGO_HOST not defined") - for attempt in range(1, ATTEMPTS + 1): + for attempt in range(1, attempts + 1): try: register_dev( dev_name="MY/BENCHMARK/DEVICE", @@ -201,14 +201,14 @@ def register_device(): ) break except Exception: - time.sleep(SLEEP) - if attempt == ATTEMPTS: + time.sleep(sleep) + if attempt == attempts: raise TimeoutError("Tango device could not be registered") @pytest.fixture(scope="session") def test_controller(tango_system, register_device): - TIMEOUT = 10 + timeout = 10 process = subprocess.Popen( ["python", HERE / "benchmarking" / "controller.py"], stdin=subprocess.PIPE, @@ -221,7 +221,7 @@ def test_controller(tango_system, register_device): while "Uvicorn running" not in ( process.stdout.readline().strip() # type: ignore ): - if time.monotonic() - start_time > TIMEOUT: + if time.monotonic() - start_time > timeout: raise TimeoutError("Controller did not start in time") # close backend caches before the event loop @@ -235,4 +235,4 @@ def test_controller(tango_system, register_device): yield process process.send_signal(signal.SIGINT) - process.wait(TIMEOUT) + process.wait(timeout) diff --git a/tests/test_backend.py b/tests/test_backend.py index 4a8389fb6..55dee4a44 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -6,7 +6,7 @@ from fastcs.controller import Controller from fastcs.cs_methods import Command from fastcs.datatypes import Int -from fastcs.exceptions import FastCSException +from fastcs.exceptions import FastCSError from fastcs.wrappers import command, scan @@ -156,7 +156,7 @@ async def test_scan_wrapper(): # This allows scan time to run await asyncio.sleep(0.2) # _scan_done should raise an exception - assert isinstance(exception_info["exception"], FastCSException) + assert isinstance(exception_info["exception"], FastCSError) for task in backend._scan_tasks: internal_exception = task.exception() assert internal_exception diff --git a/tests/test_cs_methods.py b/tests/test_cs_methods.py index cda1dd96b..24e139c2a 100644 --- a/tests/test_cs_methods.py +++ b/tests/test_cs_methods.py @@ -10,20 +10,20 @@ UnboundPut, UnboundScan, ) -from fastcs.exceptions import FastCSException +from fastcs.exceptions import FastCSError def test_method(): def sync_do_nothing(): pass - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): Method(sync_do_nothing) # type: ignore async def do_nothing_with_return() -> int: return 1 - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): Method(do_nothing_with_return) # type: ignore async def do_nothing(): @@ -50,10 +50,10 @@ async def do_nothing_with_arg(self, arg): with pytest.raises(NotImplementedError): await unbound_command() - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): UnboundCommand(TestController.do_nothing_with_arg) # type: ignore - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): Command(TestController().do_nothing_with_arg) # type: ignore command = unbound_command.bind(TestController()) @@ -79,10 +79,10 @@ async def update_nothing_with_arg(self, arg): with pytest.raises(NotImplementedError): await unbound_scan() - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): UnboundScan(TestController.update_nothing_with_arg, 1.0) # type: ignore - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): Scan(TestController().update_nothing_with_arg, 1.0) # type: ignore scan = unbound_scan.bind(TestController()) @@ -106,10 +106,10 @@ async def put_no_value(self): with pytest.raises(NotImplementedError): await unbound_put() - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): UnboundPut(TestController.put_no_value) # type: ignore - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): Put(TestController().put_no_value) # type: ignore put = unbound_put.bind(TestController()) diff --git a/tests/test_docs_snippets.py b/tests/test_docs_snippets.py index 63d63ab7f..3d2aaaf5c 100644 --- a/tests/test_docs_snippets.py +++ b/tests/test_docs_snippets.py @@ -19,14 +19,14 @@ def sim_temperature_controller(): text=True, ) - TIMEOUT = 10 + timeout = 10 start_time = time.monotonic() while process.stdout is not None: line = process.stdout.readline() if "Temperature controller running" in line: break - if time.monotonic() - start_time > TIMEOUT: + if time.monotonic() - start_time > timeout: raise TimeoutError("Simulator did not start in time") time.sleep(0.1) diff --git a/tests/transport/epics/ca/test_softioc.py b/tests/transport/epics/ca/test_softioc.py index 78ad51aac..e3975618c 100644 --- a/tests/transport/epics/ca/test_softioc.py +++ b/tests/transport/epics/ca/test_softioc.py @@ -19,7 +19,7 @@ from fastcs.controller_api import ControllerAPI from fastcs.cs_methods import Command from fastcs.datatypes import Bool, Enum, Float, Int, String, Waveform -from fastcs.exceptions import FastCSException +from fastcs.exceptions import FastCSError from fastcs.transport.epics.ca.adapter import EpicsCATransport from fastcs.transport.epics.ca.ioc import ( EPICS_MAX_NAME_LENGTH, @@ -106,7 +106,7 @@ def test_make_input_record( def test_make_record_raises(mocker: MockerFixture): # Pass a mock as attribute to provoke the fallback case matching on datatype - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): _make_record("PV", mocker.MagicMock()) @@ -206,7 +206,7 @@ def test_long_enum_validator(mocker: MockerFixture): def test_get_output_record_raises(mocker: MockerFixture): # Pass a mock as attribute to provoke the fallback case matching on datatype - with pytest.raises(FastCSException): + with pytest.raises(FastCSError): _make_record("PV", mocker.MagicMock(), on_update=mocker.MagicMock()) @@ -429,7 +429,7 @@ async def do_nothing(): ... class ControllerLongNames(Controller): attr_r_with_reallyreallyreallyreallyreallyreallyreally_long_name = AttrR(Int()) - attr_rw_with_a_reallyreally_long_name_that_is_too_long_for_RBV = AttrRW(Int()) + attr_rw_with_a_reallyreally_long_name_that_is_too_long_for_rbv = AttrRW(Int()) attr_rw_short_name = AttrRW(Int()) command_with_reallyreallyreallyreallyreallyreallyreally_long_name = Command( do_nothing @@ -466,12 +466,12 @@ def test_long_pv_names_discarded(mocker: MockerFixture): f"{DEVICE}:{short_pv_name}_RBV", **record_metadata_from_datatype( long_name_controller_api.attributes[ - "attr_rw_with_a_reallyreally_long_name_that_is_too_long_for_RBV" + "attr_rw_with_a_reallyreally_long_name_that_is_too_long_for_rbv" ].datatype ), **record_metadata_from_attribute( long_name_controller_api.attributes[ - "attr_rw_with_a_reallyreally_long_name_that_is_too_long_for_RBV" + "attr_rw_with_a_reallyreally_long_name_that_is_too_long_for_rbv" ] ), ) diff --git a/tests/transport/epics/pva/test_p4p.py b/tests/transport/epics/pva/test_p4p.py index 12460613e..83bdf9069 100644 --- a/tests/transport/epics/pva/test_p4p.py +++ b/tests/transport/epics/pva/test_p4p.py @@ -72,7 +72,7 @@ async def test_scan_method(p4p_subprocess: tuple[str, Queue]): # While the scan method will update every 0.1 seconds, it takes around that # time for the p4p backends to update, broadcast, get. - LATENCY = 1e8 + latency = 1e8 e_monitor = ctxt.monitor(f"{pv_prefix}:Child1:E", e_values.put) try: @@ -87,7 +87,7 @@ async def test_scan_method(p4p_subprocess: tuple[str, Queue]): new_raw_value = (await e_values.get()).raw assert new_raw_value["value"] is not value assert new_raw_value["timeStamp"]["nanoseconds"] == pytest.approx( - nanoseconds + 1e8, abs=LATENCY + nanoseconds + 1e8, abs=latency ) value = new_raw_value["value"] assert isinstance(value, bool) @@ -96,7 +96,7 @@ async def test_scan_method(p4p_subprocess: tuple[str, Queue]): new_raw_value = (await e_values.get()).raw assert new_raw_value["value"] is not value assert new_raw_value["timeStamp"]["nanoseconds"] == pytest.approx( - nanoseconds + 1e8, abs=LATENCY + nanoseconds + 1e8, abs=latency ) finally: diff --git a/tests/transport/graphQL/test_graphQL.py b/tests/transport/graphQL/test_graphql.py similarity index 99% rename from tests/transport/graphQL/test_graphQL.py rename to tests/transport/graphQL/test_graphql.py index 76d03d27d..b61f2495a 100644 --- a/tests/transport/graphQL/test_graphQL.py +++ b/tests/transport/graphQL/test_graphql.py @@ -15,7 +15,7 @@ from fastcs.attributes import AttrR, AttrRW, AttrW from fastcs.datatypes import Bool, Float, Int, String -from fastcs.transport.graphQL.adapter import GraphQLTransport +from fastcs.transport.graphql.adapter import GraphQLTransport class GraphQLController(MyTestController):