-
Notifications
You must be signed in to change notification settings - Fork 15
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
refactor: extract the standard variable replacement logic into its own class. #64
Conversation
Should we also move the "set var" middleware here? |
A little tricky with the saved state, but extracted some of it |
…e set_variables and replace_variables middlewares to both be handled by the VariableProcessor.
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.
Some bike shedding comments, hopefully thats not too annoying. Feel free to push back on anything.
mysql_mimic/variable_processor.py
Outdated
|
||
|
||
@dataclass | ||
class SessionContext: |
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.
Another nit, because I think this is a good opportunity to dig into python style:
SessionContext is like an interface that the VariableProcessor owns (a la dependency inversion).
And we're expecting VariableProcessor to be used in a context that isn't associated with a Session instance.
So maybe we should make this SessionContext interface narrower and easier to use?
For example, perhaps we shouldn't depend on Variables
, but rather a generic collections.abc.Mapping[str, Any]
? And the Variables
class should implement the Mapping protocol?
And maybe fields like "external_user" and "version" should have sensible defaults? I.e. maybe external_user
should default to current_user
?
Furthermore, python has duck typing. So maybe this doesn't need to be a dataclass? Rather, maybe it should be a Protocol? And the Session instance can implement that protocol and just pass itself in as the SessionContext, instead of instantiating a SessionContext instance?
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 guess my question here is that the reason we're trying to encapsulate this logic, as I understand it, isn't just the process of mapping functions in the query to various literals — it's the mapping itself that we want to share between projects, knowledge of how to handle and genericize things like CURDATE() specifically.
Is the idea that the variable processor can swap a generic set of functions to literals, and the Variables class contains the actual mapping we want to use?
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.
See what you think of this approach. I extracted the function mapping and removed the SessionContext interface, so there isn't any session-specific logic in VariableProcessor.
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 like this direction
mysql_mimic/functions.py
Outdated
from datetime import datetime | ||
|
||
|
||
class Functions(Mapping): |
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 don't think this class does anything?
I.e. should MySQLDatetimeFunctions
just implement Mapping[str, Callable[[], Any]]
?
And extending that even further - Do we MySQLDatetimeFunctions
? Maybe we just need a type alias and some constants:
Functions = Mapping[str, Callable[[], Any]]
MYSQL_DATETIME_FUNCTIONS = {
"NOW": lambda: timestamp.strftime("%Y-%m-%d %H:%M:%S"),
"CURDATE": lambda: timestamp.strftime("%Y-%m-%d"),
"CURTIME": lambda: timestamp.strftime("%H:%M:%S"),
}
MYSQL_DATETIME_FUNCTIONS.update(
{
"CURRENT_TIMESTAMP": functions["NOW"],
"LOCALTIME": functions["NOW"],
"LOCALTIMESTAMP": functions["NOW"],
"CURRENT_DATE": functions["CURDATE"],
"CURRENT_TIME": functions["CURTIME"],
}
)
Then, later in Session:
functions = {
**MYSQL_DATETIME_FUNCTIONS,
# ... session related functions
}
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.
They can't be constants, since they depend on the timestamp, but I put them in static functions.
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.
Ah, I've been missing that timestamp dependency this whole time 🤦
mysql_mimic/session.py
Outdated
@@ -164,13 +187,13 @@ class Session(BaseSession): | |||
dialect: Type[Dialect] = MySQL | |||
|
|||
def __init__(self, variables: Variables | None = None): | |||
self._variable_processor: VariableProcessor |
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 had missed the timestamp dependency. Lets do it like you had before, just instantiate it when we need it.
Background for this change: the functionality in _replace_variables_middleware is useful in other contexts for standardizing some of the sql functions in a query. Pulling it into its own class so other repos can use it. Should not change any functionality in mysql-mimic.