Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change deserialization based around ComponentConfig instances #16

Merged
merged 21 commits into from Oct 20, 2021

Conversation

thomascobb
Copy link
Contributor

@thomascobb thomascobb commented Sep 23, 2021

The YAML looks cleaner now, but there's some ugliness:

  • DeviceSimulation sets Adapter.device and Adapter.raise_interrupt, mypy complains about the latter
  • Component vs BaseComponent, mypy doesn't like returning a DeviceSimulation when the call signature says it wants a Component
  • I find myself wanting to write class TCPServer(Server). Should Server, Adapter, Component be abstract base classes instead of protocols?
  • Does the contents of configurable.py belong in component.py?

I expect there will be more issues as I convert the rest, but I wanted to PR early...

Fixes #4

@garryod
Copy link
Member

garryod commented Sep 23, 2021

  • DeviceSimulation sets Adapter.device and Adapter.raise_interrupt, mypy complains about the latter

Could we eliminate the need for this by introspecting the base class when we generate the config class?

I.e. if Adapter is:

class Adapter:
    def __init__(device: Device, raise_interrupt: Callable[[],None]) -> None:
        ...

Then, could DerivedAdapterConfig be:

class DerivedAdapterConfig(AdapterConfig):
    def __call__(device: Device, raise_interrupt: Callable[[],None]) -> DerivedAdapter:
        return DerivedAdapter(device, raise_interrupt, *vars(self))

And DeviceSimulation could take adapters: Iterable[Callable[Device, Callable[[],None], Adapter]].

  • Component vs BaseComponent, mypy doesn't like returning a DeviceSimulation when the call signature says it wants a Component

That seems rather odd and I have no immediate suggestions, though it may be a symptom of importing via typing_compat.

  • I find myself wanting to write class TCPServer(Server). Should Server, Adapter, Component be abstract base classes instead of protocols?

I think I'm on board with this, having a concrete interface would be useful for both static type checking and config generation (see above). Would like to hear @callumforrester's thoughts though.

  • Does the contents of configurable.py belong in component.py?

I don't believe so, I'm of the opinion that we make everything configurable but strongly encourage the inclusion of component configs which simplify the YAML. As such it doesn't make sense for it to be in the component module.

@thomascobb
Copy link
Contributor Author

  • DeviceSimulation sets Adapter.device and Adapter.raise_interrupt, mypy complains about the latter

Could we eliminate the need for this by introspecting the base class when we generate the config class?

The more I look at this, the more I would like to add these args into run_forever for each class. There is prior art for this in Server.run_forever:

    async def run_forever(
        self,
        on_connect: Callable[[], AsyncIterable[Optional[T]]],
        handler: Callable[[T], Awaitable[AsyncIterable[Optional[T]]]],
    ) -> None:

This would mean that __init__ for each class just takes config args, then run_forever takes the runtime context. This makes the "everything configurable" a lot easier, as they can be dataclasses, and we don't need separate Config classes anywhere. It also seems to fit with the pattern that asyncio classes require, __init__ stores args, then an async def will actually do the work.

What do you reckon?

  • I find myself wanting to write class TCPServer(Server). Should Server, Adapter, Component be abstract base classes instead of protocols?

I think I'm on board with this, having a concrete interface would be useful for both static type checking and config generation (see above). Would like to hear @callumforrester's thoughts though.

I'll setup a meeting for next week to discuss

  • Does the contents of configurable.py belong in component.py?

I don't believe so, I'm of the opinion that we make everything configurable but strongly encourage the inclusion of component configs which simplify the YAML. As such it doesn't make sense for it to be in the component module.

That makes sense

@garryod
Copy link
Member

garryod commented Sep 24, 2021

  • DeviceSimulation sets Adapter.device and Adapter.raise_interrupt, mypy complains about the latter

Could we eliminate the need for this by introspecting the base class when we generate the config class?

The more I look at this, the more I would like to add these args into run_forever for each class. There is prior art for this in Server.run_forever:

    async def run_forever(
        self,
        on_connect: Callable[[], AsyncIterable[Optional[T]]],
        handler: Callable[[T], Awaitable[AsyncIterable[Optional[T]]]],
    ) -> None:

This would mean that __init__ for each class just takes config args, then run_forever takes the runtime context. This makes the "everything configurable" a lot easier, as they can be dataclasses, and we don't need separate Config classes anywhere. It also seems to fit with the pattern that asyncio classes require, __init__ stores args, then an async def will actually do the work.

What do you reckon?

I reckon that's probably the most viable solution, thinking about it, we don't actually want to be giving an Adapter the ability to poke at a Device or raise_interrupt prior to runtime anyway. It's likely worth re-working the lifetime_runnable module to reflect this.

@garryod garryod mentioned this pull request Oct 1, 2021
@thomascobb thomascobb marked this pull request as ready for review October 13, 2021 15:59
@thomascobb
Copy link
Contributor Author

Still need to fix the docs

@thomascobb
Copy link
Contributor Author

@garryod this is ready to review

@codecov
Copy link

codecov bot commented Oct 13, 2021

Codecov Report

Merging #16 (eda8414) into master (653cc5f) will increase coverage by 4.77%.
The diff coverage is 89.12%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #16      +/-   ##
==========================================
+ Coverage   88.09%   92.87%   +4.77%     
==========================================
  Files          43       46       +3     
  Lines        1462     1417      -45     
==========================================
+ Hits         1288     1316      +28     
+ Misses        174      101      -73     
Impacted Files Coverage Δ
tickit/adapters/httpadapter.py 48.38% <43.47%> (-46.35%) ⬇️
tickit/devices/cryostream/cryostream.py 85.00% <55.00%> (-15.00%) ⬇️
tickit/core/components/system_simulation.py 71.42% <77.77%> (+14.28%) ⬆️
tickit/devices/source.py 81.81% <81.81%> (+3.24%) ⬆️
tickit/devices/cryostream/status.py 95.83% <83.33%> (+1.89%) ⬆️
tickit/core/components/component.py 97.61% <94.73%> (-2.39%) ⬇️
tickit/core/adapter.py 96.00% <95.00%> (-4.00%) ⬇️
tickit/adapters/composed.py 100.00% <100.00%> (ø)
tickit/adapters/epicsadapter.py 100.00% <100.00%> (+16.32%) ⬆️
tickit/adapters/servers/tcp.py 91.66% <100.00%> (+55.55%) ⬆️
... and 24 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 653cc5f...eda8414. Read the comment docs.

tickit/utils/configuration/loading.py Show resolved Hide resolved
tickit/utils/configuration/loading.py Show resolved Hide resolved
tickit/utils/configuration/configurable.py Show resolved Hide resolved
tickit/core/components/device_simulation.py Outdated Show resolved Hide resolved
tickit/core/components/system_simulation.py Outdated Show resolved Hide resolved
tickit/cli.py Outdated Show resolved Hide resolved
tickit/cli.py Outdated Show resolved Hide resolved
tickit/core/management/event_router.py Outdated Show resolved Hide resolved
tests/core/test_lifetime_runnable.py Outdated Show resolved Hide resolved
tests/conftest.py Show resolved Hide resolved
coretl and others added 5 commits October 19, 2021 13:53
Co-authored-by: Garry O'Donnell <56754322+garryod@users.noreply.github.com>
Co-authored-by: Garry O'Donnell <56754322+garryod@users.noreply.github.com>
Co-authored-by: Garry O'Donnell <56754322+garryod@users.noreply.github.com>
Co-authored-by: Garry O'Donnell <56754322+garryod@users.noreply.github.com>
Co-authored-by: Garry O'Donnell <56754322+garryod@users.noreply.github.com>
@garryod garryod mentioned this pull request Oct 19, 2021
@thomascobb thomascobb changed the title Try to fix #4 for cryostream only Change deserialization based around ComponentConfig instances Oct 19, 2021
@thomascobb
Copy link
Contributor Author

I think we're ready to merge unless you can see anything else to fix

Copy link
Member

@garryod garryod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, just going to change lifetime_runnable to runner since it's purpose has changed

@garryod garryod merged commit 2e41f45 into master Oct 20, 2021
@garryod garryod deleted the deserialization branch October 20, 2021 08:55
collang added a commit that referenced this pull request Oct 21, 2021
garryod added a commit that referenced this pull request Nov 5, 2021
* Install pytest-pydocstyle

* Configure pytest to use pydocstyle

* Lock with DLS PyPi

* Ignore module & package docstrings

* Fix loading docstrings

* Fix configurable docstrings

* Fix topic_naming docstrings

* Fix singleton docstrings

* Fix byte_format docstrings

* Fix source docstrings

* Fix sink docstrings

* Fix state_interface docstrings

* Bump pydocstyle to 6.1.1

* Fix kafka docstrings

* Fix internal docstrings

* Fix slave docstrings

* Fix master docstrings

* Fix base docstrings

* Fix ticker docstrings

* Fix device_simulation docstrings

* Fix system_simulation docstrings

* Fix component docstrings

* Fix typedefs docstrings

* Fix lifetime_runnable docstrings

* Fix device docstrings

* Fix adapter docstrings

* Fix tcp docstrings

* Fix regex_command docstrings

* Fix command_interpreter docstrings

* Fix composed docstrings

* Fix remote_controlled docstrings

* Fix shutter docstrings

* Fix trampoline docstrings

* Fix _version_git docstrings

* Re-lock pipfile.lock

* Add cli docstrings

* Add D418 to pydocstyle ignore

* Fix event_router docstrings

* Refactor epicsadapter

* Update EpicsAdapter adapters

* Update example configs

* Fix base docstrings

* Fix shutter docstrings

* Fix cryostream docstrings

* Fix states docstrings

* Fix status docstrings

* Enable pydocstyle linting

* Add simple docstrings to pneumatic

* Make flake8 complient

* Added docstrings to femto.py; Changed 'records' function in femto, pneumatic and epicsadapter to 'on_db_load' to better reflect what it does; Updated Pipfile to include charset-normalizer

* Fixed some docstring formatting

* Fixed line lengths

* Update tickit/devices/pneumatic/pneumatic.py

Co-authored-by: Garry O'Donnell <56754322+garryod@users.noreply.github.com>

* Removed whitespace

* Changed a couple of docstrings to include args and return values

* Remove charset-normalizer

* Edited set_state function

* Fix typechecking error

* Basic epics adapter sanity checks

* Add tests for cli.py

* Remove unused import

* Add tests for CryostreamBase class

* Improved Exception handling, Improved code readability

Base Exception classes substituted for ValueErrors where appropriate.
Hard coded RunMode values removed in favor of using the RunMode enum.
Made TODO flags fully capitalised to properly integrate with IDE.

* Add check on 'status' attribute before accessing.

Previously there was no check on whether the 'status' attribue was set
before accessing using the 'get_status' method. Now the 'get_status'
method will use the 'set_status_format' method to set the attribute it
wants to access if it is not already set.

* Adding tests for Cryostream and Cryostream Adapter

* Make pydocstyle complient

* Use absolute difference to compare gas and target temperature

* Refactoring and rewriting test

* Change to 'margin of error' style test

* Add comments

* Improve test coverage of CryostreamBase

* Refactoring of attribute and improvements 'set_current' docstring.

Renamed _current attribute to _output_current to better reflect it's
interpretation.

Improved the docstring for the 'set_current' message. Before it seemed
like the 'current' parameter would be stored in the class' current
attribute. Whereas in fact the input parameter is multiplied by the
'gain' of the device first.

* Add tests for the femto class

* Add tests for Pneumatic and PneumaticAdapter

* Rename file and increase test coverage to 100%

* Remove unused imports

* Remove git merging artifacts

* Remove more git merge artifacts

* Improvements to tests for CryostreamBase

* Improve tests by using 'await' instead of 'asyncio.run'

* Fix broken test

* Add tests for EpicsAdapter

* Add attribute in test rather than change class

* Disable eq to make dataclass InputRecord hashable

This is required to be able to use InputRecords as a key to a dict.

* Fix lack of awaiting

Co-authored-by: Garry O'Donnell <56754322+garryod@users.noreply.github.com>

* Clean commented out code

* Make flake8 complient

* Freeze dataclass instead of removing __eq__

* Remove trailing '0' from conditional in CryostreamBase.ramp

A seeming flaw in the logic for ramp where it was impossible to set the gas flow to anything other than 10 was the result of a trailing 0 on the number 9000.

* Change tests for ramp to reflect changes to that method

* Update tests to reflect changes to 'ramp' method

* Make the mocking of softioc explicitly apply to the entire test suite.

Move the mocking of softioc to tests/conftest.py where it will
explicitly apply to all test. Mark femto as xfail for now. We know it
works in isolation but we need to figure out how to get it working when
run along with the other tests.

* Remove outdated comment

* Add typehint

* Patch all softioc methods

* Remove mocking of softioc module

* Undo Garry's meddling

* Refactor for flake8 complience

* Make test skip and add an explaination for the future

* Remove redundant 'xfail'

* Undo pointless tinkering

* Extract method from 'build_ioc'

* Test extracted method instead of old method

* Have mock cryostream get status return correct mock status

* Use assert_awaited for coroutines

* Change test to suppress asyncio warning

* Revert "Change test to suppress asyncio warning"

This reverts commit e085798.

* Fix CLI tests by mocking tickit.cli.run_all_forever

* Add tests for Source and Sink devices

* Add device and system simulation tests

* Remove merging artifacts

* Mock state producer and consumer

* Declump tests

* Separate testable logic into another function

* Add tests for TcpServer

* Rename fixture and tweak the test to attempt to improve coverage.

* Add docstring to "generate_handle_function"

* Add "asyncio.streams" classes to nitpick_ignore list.

The documentation for these classes was built with an out-of-date
version of Sphinx which may have caused them to become unavailable via
the intersphinx extension.

* Enhance test with more assertions

* Enhance test with more assertions

* Enhance tests with more assertions

* Remove unintensional commenting-out of theme setting.

That setting does not work locally and that change got committed
accidentally.

* Add tests for BaseScheduler

* Refactor for easier testing

* [WIP] Adding tests for MasterScheduler

I'm going to need these files at work next week so don't panic if they
break CI okay? ;)

* Add docstrings

* Substitute set comprehension for list to preserve order

* Add working tests for multiple methods of MasterScheduler

* Remove unneccessary imports

* Change test to reflect the changes made in #16

* Update system simulation tests

* Update tests for Sink device

Changes to how sink object are intantiated calls for updates to these
tests.

* Update to tests for Source device

Updates to how Source devices are instantiated requires that these tests
be updated.

* Add 'Optional' to typing as required by 'mypy'

The origins of this error are unknown but it is a trivial change which
can be included with this PR.

* Add tests for 'SlaveScheduler'

* Rename fixtures

* Split tests for handling interrupt and output messages.

* Rename fixtures

* Changes to 'MasterScheduler' and tests

* Change name of '_run_tick' to '_do_tick'
* Moved call to 'setup' from '_do_initial_tick' to 'run_forever'
* Added test for successful scheduling of interrupts while there are
  queued wakeups

* Mollify MyPy errors

* Replace 'Internal' state interfaces with base classes

* Remove commented-out code

* Enhance testing of logging call

* Make 'generage_handle_function' "private 😏"

* Remove the '_TestBaseScheduler' class and use 'patch.object' instead

* Fix Cli tests for 'skip-lock' installations

* Fix Cli tests for 'skip-lock' installations

* Add tests for 'loading.py'

* Change to reading 'current-monitor.yaml' to avoid import errors.

* Refactor 'run_forever' into an instance method

* Add tests for 'KafkaStateConsumer/Producer'

* Test byte format (#38)

* Added tests for ByteFormat

* Remove concrete classes in favour of Mocks

* Make 'run_forever' method "private"

Co-authored-by: O'Donnell, Garry (DLSLtd,RAL,LSCI) <garry.o'donnell@diamond.ac.uk>
Co-authored-by: O'Donnell, Garry (DLSLtd,RAL,LSCI) <garry.euan.odonnell@ntlworld.com>
Co-authored-by: Ollie Copping <oliver.copping@diamond.ac.uk>
Co-authored-by: Garry O'Donnell <56754322+garryod@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(De)serialization rework
4 participants