Refactor CommandLine command registration#1611
Refactor CommandLine command registration#1611mikeroll wants to merge 9 commits intosqlalchemy:mainfrom
CommandLine command registration#1611Conversation
5d7b091 to
e1d6454
Compare
|
My bad with the type hints, should be green now. |
sqla-tester
left a comment
There was a problem hiding this comment.
OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 369059e of this pull request into gerrit so we can run tests and reviews and stuff
|
New Gerrit review created for change 369059e: https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718 |
zzzeek
left a comment
There was a problem hiding this comment.
OK well, the movement of methods and all is fine, but the goal of the PR here means that it's not that simple.
the goal is "make it easier for third parties to subclass CommandLine". This does some initial structure for that, but it doesnt do all these other things:
- document any of these methods. how would I know what _register_command does?
- make it clear which methods are safe - why are these methods underscored if they are now "public" ?
- provide an example in the docs how to subclass CommandLine
- provide any unit tests to ensure that a user-custom CommandLine implementation continues to work, otherrwise there's nothing to prevent someone changing CommandLine again here in some arbitrary way
- whether or not this is backwards compatible with someone who already did their own
CommandLine._generate_argsshould be established also, it looks like that's maintained here
overall I like making CommandLine subclassable but that's public API, and it has to be done as a public API which means: 1. docs 2. proper conventions 3. API stability 4. tests
sqla-tester
left a comment
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
code review left on gerrit
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
alembic/config.py
Outdated
| self.parser = parser | ||
|
|
||
| def _register_command(self, fn: Any) -> None: | ||
| fn, positional, kwarg, help_text = self._parse_command_from_function(fn) |
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
we could avoid returning the fn here, since it's what we provide as argument
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
alembic/config.py
Outdated
| if ( | ||
| arg == "revisions" | ||
| or fn in self._POSITIONAL_TRANSLATIONS | ||
| and self._POSITIONAL_TRANSLATIONS[fn][arg] == "revisions" |
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
we have already handle _POSITIONAL_TRANSLATIONS so I don't think we need this or part
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
There was a problem hiding this comment.
True, I've removed this.
alembic/config.py
Outdated
| fn.__name__, help=" ".join(help_text) | ||
| subparser.add_argument( | ||
| "revisions", | ||
| nargs="+", |
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
let's parametrize this using a dict.
we could rename _POSITIONAL_HELP and make it a dict of dicts that with the kwargs for the add_argument
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
There was a problem hiding this comment.
Good idea, we now have _POSITIONAL_OPTS for this.
alembic/util/compat.py
Outdated
| ep = importlib_metadata.entry_points() | ||
| if hasattr(ep, "select"): | ||
| return ep.select(group=group) | ||
| return cast(Sequence[EntryPoint], ep.select(group=group)) |
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
strange that this is now needed
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
There was a problem hiding this comment.
Yeah I though I had to fix this now, but turns out I was running different python + mypy versions than CI. Reverted this.
|
@zzzeek Thanks for your thoughtful comments! In this PR I actively avoided doing all the "public interface" work that you mentioned in points 1-4. To be honest, I was intially going to stop at that, but I fully agree that the new interface eventually has to be public to be of any real value. Regarding point 5 though: yes, the changes to Given that you've approved this already - I take it you'd be fine with merging this and following up with the "public API" parts later on? |
sqla-tester
left a comment
There was a problem hiding this comment.
OK, this is sqla-tester setting up my work on behalf of CaselIT to try to get revision ed96df2 of this pull request into gerrit so we can run tests and reviews and stuff
|
Patchset ed96df2 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718 |
sqla-tester
left a comment
There was a problem hiding this comment.
OK, this is sqla-tester setting up my work on behalf of CaselIT to try to get revision 3006935 of this pull request into gerrit so we can run tests and reviews and stuff
|
Patchset 3006935 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718 |
sqla-tester
left a comment
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
code review left on gerrit
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
- alembic/util/compat.py (line 59): Done
| subparser.set_defaults(cmd=(fn, positional, kwarg)) | ||
| ) | ||
| ) | ||
|
|
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
Done
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
| for arg in kwarg: | ||
| if arg in self._KWARGS_OPTS: | ||
| kwarg_opt = self._KWARGS_OPTS[arg] | ||
| args, opts = kwarg_opt[0:-1], kwarg_opt[-1] |
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
Done
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
alembic/config.py
Outdated
| subparser.add_argument(*args, **opts) # type:ignore | ||
|
|
||
| for arg in positional: | ||
| if arg in self._POSITIONAL_OPTS: |
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
Done
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
zzzeek
left a comment
There was a problem hiding this comment.
hi my comment is not linkable so reiterating here, this patch has good ideas but they need to be presented as stable API that isn't going to change
my comment is below. at the very least this needs something with public methods, perhaps a UserDefinedCommandLine subclass? with docstrings, and a bit of a test to make sure we dont accidentally change that API - otherwise the submitter here is better off vendoring their own changes locally since they are essentially private:
OK well, the movement of methods and all is fine, but the goal of the PR here means that it's not that simple.
the goal is "make it easier for third parties to subclass CommandLine". This does some initial structure for that, but it doesnt do all these other things:
document any of these methods. how would I know what _register_command does?
make it clear which methods are safe - why are these methods underscored if they are now "public" ?
provide an example in the docs how to subclass CommandLine
provide any unit tests to ensure that a user-custom CommandLine implementation continues to work, otherrwise there's nothing to prevent someone changing CommandLine again here in some arbitrary way
whether or not this is backwards compatible with someone who already did their own CommandLine._generate_args should be established also, it looks like that's maintained here
overall I like making CommandLine subclassable but that's public API, and it has to be done as a public API which means: 1. docs 2. proper conventions 3. API stability 4. tests
no, sorry, I dont know how "approve" got in there. as mentioned, this PR so far serves your immediate use case, but nobody else's since it's not documented, and there's no way to prevent someone from changing this API in the future which would break your code also without tests for API stability |
|
literally, do this:
that's it! |
|
but also make sure the old |
|
Added a test, docstrings and a cookbook recipe. Halfway through writing the docs I realized there's absolutely no need for subclassing as |
|
thank you! |
sqla-tester
left a comment
There was a problem hiding this comment.
OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 2e0df01 of this pull request into gerrit so we can run tests and reviews and stuff
|
Patchset 2e0df01 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718 |
sqla-tester
left a comment
There was a problem hiding this comment.
OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 2d4e7db of this pull request into gerrit so we can run tests and reviews and stuff
|
Patchset 2d4e7db added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718 |
sqla-tester
left a comment
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
let's restore that minor point, otherwise it's fine for me
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
| subparser.set_defaults(cmd=(fn, positional, kwarg)) | ||
|
|
||
| for arg in kwarg: | ||
| if arg in self._KWARGS_OPTS: |
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
we don't add unknown args here, it's not ideal in the public case, but I guess it's fine for now.
IIRC just adding all kwargs would not work since alembic has some of these "non-cli" args.
I guess in the future we could add additional args to this function to support extra args
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
alembic/config.py
Outdated
| for arg in positional: | ||
| if arg in self._POSITIONAL_OPTS: | ||
| opts = self._POSITIONAL_OPTS[arg] | ||
| subparser.add_argument(arg, **opts) # type:ignore |
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
this is a behavior change: before even arguments that were outside the _POSITIONAL_OPTS were added.
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
sqla-tester
left a comment
There was a problem hiding this comment.
OK, this is sqla-tester setting up my work on behalf of CaselIT to try to get revision d2ffce0 of this pull request into gerrit so we can run tests and reviews and stuff
|
Patchset d2ffce0 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718 |
sqla-tester
left a comment
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
thanks, it's ok for me now!
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
| for arg in positional: | ||
| opts = self._POSITIONAL_OPTS.get(arg, {}) | ||
| subparser.add_argument(arg, **opts) # type:ignore | ||
|
|
There was a problem hiding this comment.
Federico Caselli (CaselIT) wrote:
Done
View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718
|
Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5718 has been merged. Congratulations! :) |
Added new :meth:`.CommandLine.register_command` method to :class:`.CommandLine`, intended to facilitate adding custom commands to Alembic's command line tool with minimal code required; previously this logic was embedded internally and was not publicly accessible. A new recipe demonstrating this use is added. Pull request courtesy Mikhail Bulash. Fixes: #1610 Closes: #1611 Pull-request: sqlalchemy/alembic#1611 Pull-request-sha: d2ffce008317508449de9bd0bce3ff9076c5d90f Change-Id: Icab9978357915e81bb52a9f0717f21d5ee7f8341
Fixes: #1610
Refactors
CommandLineinternals so that it's easier to plug custom commands into the cli, in addition to the builtinsalembic.command.No public interface changes whatsoever.
The topic was somewhat discussed here #1456
Description
The point from the linked discussion still applies - this does not enable any kind of plugin system, one would still need to have their own subclass of
CommandLineand call it from their own script. However, the implementation of such a subclass should be much cleaner now. I would expect it to look like so:Checklist
This pull request is:
must include a complete example of the issue. one line code fixes without an
issue and demonstration will not be accepted.
Fixes: #<issue number>in the commit messageThis is purely internal refactoring, so no new tests are required. For extra confidence I:
toxlocally;alembic --helpbefore and after this change and made sure there was no diff.Have a nice day!