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

refactor: extract the standard variable replacement logic into its own class. #64

Merged
merged 11 commits into from
Jan 17, 2025

Conversation

cecilycarver
Copy link
Collaborator

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.

mysql_mimic/variables_processor.py Outdated Show resolved Hide resolved
mysql_mimic/variables_processor.py Outdated Show resolved Hide resolved
mysql_mimic/variables_processor.py Outdated Show resolved Hide resolved
mysql_mimic/session.py Outdated Show resolved Hide resolved
@barakalon
Copy link
Owner

Should we also move the "set var" middleware here?

@cecilycarver
Copy link
Collaborator Author

Should we also move the "set var" middleware here?

A little tricky with the saved state, but extracted some of it

mysql_mimic/session.py Outdated Show resolved Hide resolved
cecily_carver added 3 commits January 14, 2025 13:49
…e set_variables and replace_variables middlewares to both be handled by the VariableProcessor.
Copy link
Owner

@barakalon barakalon left a 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 Show resolved Hide resolved


@dataclass
class SessionContext:
Copy link
Owner

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?

Copy link
Collaborator Author

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?

Copy link
Collaborator Author

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.

Copy link
Owner

@barakalon barakalon left a 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

from datetime import datetime


class Functions(Mapping):
Copy link
Owner

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
}

Copy link
Collaborator Author

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.

Copy link
Owner

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/variable_processor.py Show resolved Hide resolved
@@ -164,13 +187,13 @@ class Session(BaseSession):
dialect: Type[Dialect] = MySQL

def __init__(self, variables: Variables | None = None):
self._variable_processor: VariableProcessor
Copy link
Owner

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.

@cecilycarver cecilycarver merged commit c65c0cb into main Jan 17, 2025
7 checks passed
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.

2 participants