- Added prompt symbol to CLU CLI and other small improvements.
- Do not cancel or timeout a command if already done.
- Add typing for the
CluGroup
decorator. - Update
sdsstools
to 1.9.1.
- Do not require keywords when the schema passed is a Pydantic model.
- Do not hardcode
is_file=True
inBaseActor.load_schema()
.
- Removed the
loop
argument fromBaseClient
. - Renamed
clu.model.BaseModel
toCluModel
to avoid confusion withpydantic.BaseModel
.
- #121 Support passing a
pydantic.BaseModel
subclass toModel
.
- Use
datetime.datetime.now()
instead ofutcnow()
. - Prevent case when incoming message headers may not be present.
- Change the error message when a command does not exist or cannot be parsed by
ClickParser
.
- Add the option to raise the error when a command fails in
ClickParser
instead of only logging it.
- Use
uv
for packaging and update workflows.
CluGroup
now inherits fromClickAliasGroup
to allow for aliases in the CLI.
- Removed use/setting of the event loop in
BaseClient.__init__()
.
- AMQP
REPLY
log messages now include the header of the message.
- #120 Callbacks that are synchronous functions are now called immediately instead of being scheduled with
call_soon()
.
- Log the command string when a new command is received by an actor.
- #119 If an exception object is passed to
BaseActor.write()
, the filename and line number where the exception where raised are included in the output. The user can choose what traceback frame to output by passingtraceback_frame
towrite()
.
- Handle cases when a message tries to be pushed to an non-existing exchange.
- Update workflows and docs building.
- Prevent multiple reconnections when using the
AMQPClient
context manager.
AMQPClient
now supports an asynchronous context manager that starts the client and closes the connection on exit.
- Added support for tasks. Tasks are coroutines that can be commanded remotely and receive a simple dictionary payload. They don't provide command completion tracking or replies to users. See more here.
- In
ActorHandler
, error messages are output as"e"
instead of as"f"
. - Start the file logging even if a custom log instance is provided to
BaseClient
.
- Use
ruff
for linting. - Use
sphinx_autodoc_typehints
and updated pygments in documentation.
- Added
write_to_log
argument toBaseClient.write()
to decide whether a reply should be written to the log. Changed several commands (get-command-model
,help
) to emit replies without logging them.
- Updated
unclick
to 0.1.0b5.
- Allow to add a model to a
ModelSet
after initialisation. - Flush messages after printing them to the CLU CLI.
- #117 Added a
WebsocketServer
class to implement a pass-through websocket client-server connection to the AMQP exchange.
AMQPClient
can be called without aname
, in which case a unique random one will be generated.
- Prevent commands sends to the CLU CLI to block until finished.
- Remove pin on
setuptools
version.
- Added
AMQPClient.add_reply_callback()
which allows to register callback functions that are called with anAMQPReply
object every time a reply is received. These replies are not filtered and the callback is called for each valid reply.
- By default
AMQPClient.send_command()
will await the command itself when the method is awaited. This means thatcmd = await client.send_command('my_actor', 'ping')
will await until theping
finishes and the replies are received. The previous behaviour can be forced withawait_command=False
, e.g.,cmd = await (await client.send_command('my_actor', 'ping', await_command=False))
. In general this should not be a breaking change since awaiting a done command will return immediately, but it's marked as breaking since in some corner cases there could be some unexpected behaviour.
- Added an
internal
argument inCluCommand
that marks the received command as internal. - Bump
unclick
to0.1.0b4
.
- When a command status changes to running, the status is emitted as internal.
- Fixed asserting of
command_id
anduser_id
in testing module.
- Removed support for Python 3.7.
- Support Python 3.11.
- Use
aio_pika>=9.0.0
andaiormq>=6.6.4
. - Support internal replies that are not shown in the CLI. Modified some command to use internal replies.
- Added
get-command-model
command that uses unclick to return a JSON representation of a command or the entire command parser. This can be used to create a programmatic API that interfaces with an actor using command strings. - Allow to filter by actor(s) in the CLI.
- Added
MessageCode
enumeration.
- The
click
context object can now be access asCommand.context
if theclick
parser is being used.
- Deal with replies without value in
MockReplyList
.
- #111 If a
LegacyActor
uses the Click parser, and a connection to Tron is defined, adds atron-reconnect
command that can be used to force recreating the connection from the actor to Tron.
- Avoid setting the event loop on init in
CallbackMixIn
. This caused the event loop to not be running in some cases whennotify()
was invoked. - Reworked how
TronConnection
uses the reconnecting protocol so that it actually reconnects.
- #110 If the content of a keyword in
LegacyActor
is empty, output the keyword without the equal sign (e.g.,test
instead oftest=
).
- Removed the
create_setup.py
file. Poetry should now support all its use-cases.
- Added a
KeywordStore
that stores each time a keyword was output. It can be enabled by passingstore=True
when instantiating an actor and accessed asactor.store
. See the documentation for more details. - Added
cancel_command()
,get_current_command_name()
, andget_current_command_name()
to click parser.
- The client model for an actor is now updated if the actor outputs a property that is not in its schema. This is not necessarily a breaking change, but it modifies the expectation that the client will validate replies against the actor schema. With this change we indicate that validating and respecting its own schema is a task for the actor and not for the client. There are at least a couple cases for which having the client enforce the actor schema was problematic: if the actor has
patternProperties
those are legal properties but are not updated in the client model; and if the actor decides to output a non-validated message. With this change, if the client receives a property that is not in the actor schema, a newProperty
is created int eh actorModel
and its value is updated. ThatProperty
hasProperty.in_schema=False
to indicate that the property has not been validated. Note that this change does not affect theTronConnection
models since those are defined by actorkeys.
Command.write()
can now be called with alogging
level instead of a string message code. For exampleCommand.write(logging.DEBUG, text="Hi")
is equivalent toCommand.write("d", text="Hi")
.
- Fixed a
ConnectionResetError
when closing the connection to aTCPStreamServer
.
- The commander of a command sent to Tron is now
actor.actor
by default, instead ofactor.actor.target_actor
. - Trying to change the status of a done command will issue a
CluWarning
and return, instead of raising aRuntimeError
.
- Prompted by COS-74, it was possible for a reply from a command with the same MID but different commander to be processed and mark a running command as complete. Now we are checking now only the MID but the commander as well.
- New
Command.child_command()
method that allows to run a child command in the same actor already runningCommand
. The practical effect is to run another of the same actor's commands as if it were part of the currentCommand
.
- Allow AMQP clients to listen to their own replies. This allows an actor sending a command to itself to know when the command is done.
Command.send_command()
now accepts anew_command
argument. Whennew_command=True
, the new command will receive a new command ID and the commander ID will be the actor running the command (to all effects this is equivalent toBaseClient.send_command()
). Ifnew_command=False
, the command ID and commander of the current actor will be used. In this case, and if the target actor is the same actor running the current command, consider usingCommand.child_command()
instead.
- Actors can use
set_message_processor()
to set a function that will receive the message dictionary before being output to the users and can make modification on before it's emitted. This is useful, for example, to improve compatibility in actors that can be run as legacy or AMQP actors.
- Relax sdsstools dependency to
>=0.4.13
. This is desirable since sdsstools is not yet at the 1.0 level and minor version changes block poetry.
- #107 Add an alias
Reply.body
toReply.message
to allow backwards compatibility.
Command.replies
is now a list ofReply
instances with aget
method. Replies are unified for all kinds of clients.
- #106
Command
now accepts atime_limit
argument that will mark it asTIMED_OUT
and done after an interval.time_limit
can also be passed tosend_command()
.
Command.actor
is always typed as an actor type.
- Added a
FakeCommand
that writes to the log and that can be used when a command may not be present.
- #104 The existing
@cancellable
decorator did not work in subcommands. The decorator has been removed and now it's possible to passcancellable=True
to the command decorator (e.g.,@command.parser.command(cancellable=True)
). This takes care of adding a--stop
option to the command. The underlying behaviour of command cancellation has not changed.
- Do not try to update model when a keyword from Tron cannot be parsed.
- Do not use a task to update the model. This caused failures when messges where being sent from a function called with an executor.
- Fixed a bug in which commanders with multiple dot-separated components would not be correctly interpreted by
handle_reply()
and the CLI. - Command code
e
does not fail the command. This prevents cases of finishing/failing the command after it has already been failed. - Improved the performance of the CLI by outputting all the parts of the message at once.
- Support Python 3.10.
- #101 The legacy actor now accepts command strings with a commander id (e.g.,
APO.Jose 10 status
). The commander id is stored in theCommand
object. Added aCommand.send_command()
that will call the remote command propagating the commander ID. For example, if a command has commanderAPO.Jose
and sends a command toguider
, theguider
actor will receive a command with commanderAPO.Jose.guider
. - #102 When calling a model or property callback, ensure that the arguments sent are a frozen copy of the state of the model/property at the time of the callback. This prevents that if a model is updated twice in quick succession, the callback may receive only the second value. A consequence of this change is that the model callback now receives a flattened dictionary with all the model keywords as the first argument.
- If not specified, a message with code
e
orf
, or an exception, will use the keyworderror
.
- #99 Add
exception_module
value to the output of an exception. - sdss/sdsstools#29 Allow to pass a custom PyYAML loader in
from_config()
that will be forwarded toread_yaml_file()
. - Actors now accept
<actor> --help
with the same result as<actor> help
. - Add
additional_properties
parameter toLegacyActor
. - When a legacy actor starts, if there is a
TronConnection
available it will try to send ahub startNubs <actor>
to initiate the connection.
- Use
clu.client
forTronConnection.send_command()
. - If a command in a
LegacySurvey
actor is left running after the client closes the connection, it would still try to output messages to it, causing asocket.send() raised exception
error. Now if the client exists the command continues running, but outputs to that client are ignored.
- #98 Add
unique()
andcancellable()
decorators for Click command parsers.
- #95 Cast all arguments to string in
ProxyClient.send_command()
. - Add
get_keys
parameter toLegacyActor.start()
that is passed toTronConnection.start()
- Use
.client
as default commander forTronConnection
and{actor}.{target}
for actor.
- Avoid and error in the callback when a connection to the TCP server is closed.
- #86
additionalProperties
is set tofalse
by default if not specified, including ifschema=None
when initialising an actor.
- #85 Added
BaseClient.proxy()
method.
- #90 If an exception object is passed as a keyword in a command or actor message, it will be unpacked into the exception type and message.
- Make the error output when a reply fails to validate more clear.
- #91 Documentation example for testing with CLU.
- Add
invoke_mock_command()
stub method toBaseClient
to simplify type checking.
LegacyActor
now accepts theconfig
parameter sent byfrom_config()
.
- Subcommands now won't write to the users when they start running.
Command
andBaseActor.write()
now accept asilent
argument that ifTrue
will execute the command normally and update the status and internal model, but won't write to the user. Timed command can be run in silent mode the first iteration by initialising them withfirst_silent=True
.
- #77 Child commands will never emit
:
orf
messages that may be confused as the parent being done. - Timed commands are run immediately when started.
from_config()
now passes the configuration to the client__init__()
so that it is accessible during initialisation.- If a timed command takes longer to run than the interval at which the poller checks if new timed commands should be run, prevent it from being issued multiple times.
- Revert previous changes to the typing of
Command
that were causing problems, but keep the generic for the command future.
- Correctly assign the type of the actor in a
Command
. - Fix error when
TopicListener.stop()
is called and there is not an active connection.
Device.start()
now returnsself
.- #84
send_command
now accepts multiple arguments before the keyword arguments. If they are passed, they will be concatenated to create the full command string. For example:client.send_command('my_actor', 'sum', '-v', 2, 4, command_id=5)
is equivalent toclient.send_command('my_actor', 'sum -v 2 4', command_id=5)
- #82
send_command
now accepts acallback
argument. If set, the callback will be called each time the actor replies and will receive the reply itself (AMQPReply
in case ofAMQPClient/Actor
andclu.legacy.types.messages.Reply
forTron/TronConnection
). Thanks to Florian Briegel for the idea.
- #81 Improve typing of
BaseCommand
and command replies. - In
TronConnection
, do not fail with aParseError
if one of the keywords cannot be parsed. Instead, issue a warning and move on to the next one. - The CLI now checks that the preferred style (
solarized-dark
) is available. Otherwise defaults topygments
default style. - Copy
Property
before notifying the callbacks. This prevents the value passed being updated in the time that it takes for the callback to go out. Property
,BaseModel
, andTronModel
now have alast_seen
attribute that is updated with the Unix time when the model or property/key are updated.- When the AMQP client is handling replies for a command, it will update the status every time it changes, not only when it is done or failed.
StatusCommand
callbacks now receive the status itself as an argument.
- When tracking the status of a command sent to Tron, update the status with each received reply, and store all the replies.
- When
as_complete_failer
cancels the tasks after an exception, suppress all possible exceptions, not onlyCancelledError
, since the original exception will be raised again. Add tests foras_complete_failer
.
- #78 Fixes a bug in which an actor with a defined
TronConnection
that had failed to start would still try to send commands to Tron.
- #79
TronConnection
now uses aReconnectingTCPClientProtocol
that will try to keep the socket to Tron open, allowing Tron to restart without losing connection.
releases
was misbehaving once we reached1.x
, and its interpretation of semantic versioning was a bit too extreme. Instead, we are now using a Markdown file withmyst-parser
. The previous changelog is still available here.
BaseActor
receives avalidate
parameter that can be used to globally define whether the actor should validate its own messages against the model.
- Transition CLU to stable!
- Upgrade
click
to^8.0.0
.
The changelog for versions previous to 1.0.0 can be found here.