-
Notifications
You must be signed in to change notification settings - Fork 6
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
rename unclear expressions #695
Conversation
the error is just the 'TaskRequest': {
? -------------------------------
|
0b2a78d
to
fd0787c
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #695 +/- ##
=======================================
Coverage 92.32% 92.33%
=======================================
Files 35 35
Lines 1799 1800 +1
=======================================
+ Hits 1661 1662 +1
Misses 138 138 ☔ View full report in Codecov by Sentry. |
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.
Thank you for the PR @stan-dot; there are some great suggestions here that will definitely improve the code. I noticed a few suggestions that seem to reflect personal coding preferences or may stem from unfamiliarity with some Python-specific practices. I’ve offered some gentle feedback on those points to clarify the current approach and rationale.
src/blueapi/client/client.py
Outdated
@@ -40,17 +40,16 @@ def __init__( | |||
@classmethod | |||
def from_config(cls, config: ApplicationConfig) -> "BlueapiClient": | |||
rest = BlueapiRestClient(config.api) | |||
events: EventBusClient | None = None |
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.
Should: This is an extra unnecessary assignment compared to the else
statement, please restore
src/blueapi/client/client.py
Outdated
stomp_client = StompClient.for_broker( | ||
broker=Broker( | ||
host=config.stomp.host, | ||
port=config.stomp.port, | ||
auth=config.stomp.auth, | ||
) | ||
) | ||
events = EventBusClient(template) | ||
else: | ||
events = None | ||
events = EventBusClient(stomp_client) |
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.
Could: Wrap this:
events = EventBusClient(StompClient.for_broker(...))
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.
why wrap this?
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.
Not a strong preference, just suggest giving it a go and seeing if it reads better
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.
for me it's clearer when the two assignments are separate
src/blueapi/client/client.py
Outdated
@@ -197,7 +196,7 @@ def run_task( | |||
|
|||
complete: Future[WorkerEvent] = Future() | |||
|
|||
def inner_on_event(event: AnyEvent, ctx: MessageContext) -> None: | |||
def _generic_event_callback(event: AnyEvent, ctx: MessageContext) -> None: |
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.
Should: I'm happy to accept that inner_on_event
is not a very clear name, but I don't think this either. Also you don't need an underscore at the front, it is already private by virtue of being a nested function. How about just on_event
or for_each_event
?
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.
Also you don't need an underscore at the front, it is already private by virtue of being a nested function
yes but for redundancy underscore is more signal
on_event
is good
src/blueapi/client/event_bus.py
Outdated
@@ -17,23 +17,23 @@ def __init__(self, message: str) -> None: | |||
|
|||
|
|||
class EventBusClient: | |||
app: StompClient | |||
facade: StompClient |
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.
Should: Not quite, if anything, the outer class is a facade. Could make this private and call it _client
, _inner_client
or _stomp_client
?
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.
_stomp_client
the best
src/blueapi/service/runner.py
Outdated
@@ -141,7 +140,7 @@ def _rpc( | |||
**kwargs: Any, | |||
) -> T: | |||
mod = import_module(module_name) | |||
func: Callable[P, T] = _validate_function( | |||
func: Callable[..., T] = _validate_function( |
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.
Should: This is less type safe since mypy/pyright won't check the function parameters, see docs, please revert.
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.
there was an error in the IDE
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.
What was the error? Did you understand what was causing it?
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.
the typing system was not happy, can look into what exactly it is
src/blueapi/worker/task.py
Outdated
""" | ||
Task that will run a plan | ||
Task that will run a plan, different from the WorkerTask |
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.
Should: I don't mind this addition as long as you explain how it is different from the WorkerTask
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.
ok
src/blueapi/worker/task.py
Outdated
@@ -10,9 +10,9 @@ | |||
LOGGER = logging.getLogger(__name__) | |||
|
|||
|
|||
class Task(BlueapiBaseModel): | |||
class TaskRequest(BlueapiBaseModel): |
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.
This one I am unsure about, this class is definitely more than a request since it has a method to actually run the task, but maybe it shouldn't? Maybe they should be separated? Thoughts welcome.
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.
definitely confusing when we have WorkerTask
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.
Agreed, I think do_task()
should be extracted somewhere else, could you make a separate issue for that please?
src/blueapi/worker/task_worker.py
Outdated
# Define filters for each status | ||
status_filter = { | ||
TaskStatusEnum.RUNNING: lambda task: not task.is_pending | ||
and not task.is_complete, # noqa: E501 | ||
TaskStatusEnum.PENDING: lambda task: task.is_pending, | ||
TaskStatusEnum.COMPLETE: lambda task: task.is_complete, | ||
}.get(status, lambda task: False) # Default to a filter that returns no tasks | ||
|
||
# Apply the filter to _tasks values | ||
return [task for task in self._tasks.values() if status_filter(task)] |
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.
Should: I don't think this any clearer, please revert? Alternatively see if it's any better with match
statements
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 think it's clearer. therefore it's a Pareto improvement in aggregate perception of clarity
src/blueapi/service/interface.py
Outdated
if stomp_config is None: | ||
return None |
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.
Should: Please revert this approach
I'm picking this out as an example but there are many others in this PR where you achieve exhaustiveness via early returns rather than explicit flow control i.e.
def foo() -> int:
if bar:
return 2
- else:
- return 5
+ return 5
This is quite an opinionated change and contrary to the approach used so far in the codebase. In reality the above diff makes no functional change and, in my view, is just less readable because it removes context and connecting words.
I imagine there's a fair bit of literature about this arguing for different patterns and paradigms but it would be a bit bike sheddy to go through it all. I would suggest just trying to be consistent with the codebase as you found it. If you really find the pattern that's there unreadable, please explain why and we can try to figure out how to make things better.
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.
you achieve exhaustiveness via early returns rather than explicit flow control
yes this is on purpose
but ok if this is in the codebase it makes no sense to fight it
but
In reality the above diff makes no functional change
that's the defintion of refactoring
connecting words
different logical branches are decoupled, they don't need connection
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.
The goal isn't to write as few words as possible it's just that
if foo:
return 1
else:
return 2
reads better than
if foo:
return 1
return 2
Because it translates more directly into natural language
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.
reads better than
that's subjective
'if-else' assumes it's 2 branches. 'early return' reads the same when the number of branches rises, and is more appropriate than a switch statement in cases when the branches aren't very cleanly different based on typeof someVariable
.
`if-else', if more branches grow becomes a hard structure to track.
furthermore
translates more directly into natural language
it's not obvious whether that is good or bad...
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.
As the number of conditions and branches increases, the code becomes harder to understand and maintain.
This is very much a straw-man argument.
if foo:
print("foo")
- else:
- print("bar")
+ print("bar")
The number of branches here doesn't change just by removing the word "else", it's just that the existence of one is now implicit rather than clearly specified. Adding more words does not always make something harder to understand.
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 clearly didn't state my argument well enough.
trying again ...
for any location in code where business logic has multiple branches, that must be reflected in the code.
patterns are many: switch statements, wrapping callbacks in a dictionary (like for filter_by_status), if + early return, if-else
.
Rust uses switch-y pattern matching very often.
in Python when we have 10 branches we'll almost always go for switch, which would feel cumbersome if there was just 2.
early return, which is my favourite isn't as great if there are 10 branches, visually, scales better than if-else
with early return when I read a function I keep track of branches and possible end-states of the function.
Every if with early return opens one possible end state and then closes it in the same place.
if a:
do_x()
return
if b:
do_y()
do_z()
with if-else
it's easier to get confused as each layer cuts in 2 the possibility space and as a reader I now need to track 2^n possibilities and see if the writer accounted correctly for all of them.
therefore the 'as the number of branches increases' refers to both lifetime of the codebase and the distribution of business logic instances in complexity from the start.
early return is more futureproof and a habit to avoid making if-else where some of the possiblities aren't addressed
fd0787c
to
ad46d8a
Compare
branch has been reset since
No description provided.