-
Notifications
You must be signed in to change notification settings - Fork 239
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
Add property for optional runner #913
Conversation
20c6f20
to
a801978
Compare
a801978
to
46ecb53
Compare
I wonder if it's better to change the signature instead |
That would be cleaner for sure, I didn't want to change anything that could potentially break someones workflow. I don't know if it would be an issue but potentially someone could do something like this now: nr = Nornir()
my_nr = nr.with_runner(runner=xyz) My guess is that most people would be using Alternatively load_runner could be moved so that it's called here again if runner isn't set. self.runner = runner or load_runner(self.config) |
I honestly would consider this a bug. It's also a typing issue so it will only break linters/development, it shouldn't break the runtime, should it? |
It's a typing issue but depending on how it's solved it could be problematic in some use cases. def __init__(
self,
inventory: Inventory,
config: Optional[Config] = None,
data: Optional[GlobalState] = None,
processors: Optional[Processors] = None,
runner: Optional[RunnerPlugin] = None,
) -> None:
self.data = data if data is not None else GlobalState()
self.inventory = inventory
self.config = config or Config()
self.processors = processors or Processors()
self.runner = runner The cleanest way to change this would be to make def __init__(
self,
inventory: Inventory,
runner: RunnerPlugin, <--- Change
config: Optional[Config] = None,
data: Optional[GlobalState] = None,
processors: Optional[Processors] = None,
) -> None:
self.data = data if data is not None else GlobalState()
self.inventory = inventory
self.config = config or Config()
self.processors = processors or Processors()
self.runner = runner An alternative would be: def __init__(
self,
inventory: Inventory,
config: Optional[Config] = None,
data: Optional[GlobalState] = None,
processors: Optional[Processors] = None,
runner: Optional[RunnerPlugin] = None,
) -> None:
self.data = data if data is not None else GlobalState()
self.inventory = inventory
self.config = config or Config()
self.processors = processors or Processors()
self.runner = runner or load_runner(self.config) <--- Change |
got it, I am starting to think the proposal on this PR is probably the best option. I don't like it is a runtime issue but we'd have to change the signature for this to be a "clean" fix as that's probably not ideal... |
nornir/core/__init__.py
Outdated
@@ -168,6 +169,13 @@ def close_connections_task(task): | |||
|
|||
self.run(task=close_connections_task, on_good=on_good, on_failed=on_failed) | |||
|
|||
@property | |||
def _runner(self) -> RunnerPlugin: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't we do the opposite? store in _runner
and have the propery be runner
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that first however it breaks some other things, in the below functions we use self.__dict__
which causes issues with an attributed such as self._runner
as we'd send in "_runner" as an argument to Nornir instead of "runner".
def with_processors(self, processors: List[Processor]) -> "Nornir":
"""
Given a list of Processor objects return a copy of the nornir object with the processors
assigned to the copy. The orinal object is left unmodified.
"""
return Nornir(**{**self.__dict__, **{"processors": Processors(processors)}})
def with_runner(self, runner: RunnerPlugin) -> "Nornir":
"""
Given a runner return a copy of the nornir object with the runner
assigned to the copy. The orinal object is left unmodified.
"""
return Nornir(**{**self.__dict__, **{"runner": runner}})
def filter(self, *args: Any, **kwargs: Any) -> "Nornir":
"""
See :py:meth:`nornir.core.inventory.Inventory.filter`
Returns:
:obj:`Nornir`: A new object with same configuration as ``self`` but filtered inventory.
"""
b = Nornir(**self.__dict__)
b.inventory = self.inventory.filter(*args, **kwargs)
return b
What we could do instead is to replace self.__dict__
in these cases with a property that returns a dictionary where the keys are hardcoded instead, i.e: inventory, data, config, runner, processors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, either that or just a cleaner clone method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can update the PR to add something like that. Within that property or method I will have to call self._runner
but that might be the best option for now. Would be nice too then go back and delete this when there's a new major version to clean it up a bit..
3ec08a6
to
ce52022
Compare
The
runner
argument to Nornir is defined as being optional but we later use it as if it's always set. Adding a property that returns aRunnerPlugin
or raises an error, with this we can also remove thestrict_optional = False
bypass in the mypy config.