Fixes #822
Implements a `.cwd` suboption for post-write hooks.
For example, one can do
```ini
[post_write_hooks]
hooks = pre-commit
pre-commit.type = console_scripts
pre-commit.entrypoint = pre-commit
pre-commit.options = run --files REVISION_SCRIPT_FILENAME
pre-commit.cwd = %(here)s
```
This feature is illustrated by the last line above.
### Description
* Improve the names of some variables recently created in #820 in order to make the code more readable.
* Add `cwd` as an option which is passed directly into `subprocess.run()`
* Add a brief description to the documentation, together with an example with `pre-commit`.
<!-- Describe your changes in detail -->
### Checklist
<!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once)
-->
This pull request is:
A new feature implementation
- [X] please include the issue number, and create an issue if none exists, which must
- [X] include a complete example of how the feature would look.
- [X] Please include: `Fixes: #<issue number>` in the commit message
- [x] **please include tests.**
**Help requested:** I can't think of any simple way to write a test. Does anyone have a suggestion???
Closes: #823
Pull-request: https://github.com/sqlalchemy/alembic/pull/823
Pull-request-sha:
9a694a7fdfc55160d1e124f6119a7a5677ce019a
Change-Id: Iacbb06d52acc362588cff2cdfec442393be8594a
)
-def _parse_options(options_str, path):
+def _parse_cmdline_options(cmdline_options_str, path):
"""Parse options from a string into a list.
Also substitutes the revision script token with the actual filename of
If the revision script token doesn't occur in the options string, it is
automatically prepended.
"""
- if REVISION_SCRIPT_TOKEN not in options_str:
- options_str = REVISION_SCRIPT_TOKEN + " " + options_str
- options_list = shlex.split(options_str)
- options_list = [
- option.replace(REVISION_SCRIPT_TOKEN, path) for option in options_list
+ if REVISION_SCRIPT_TOKEN not in cmdline_options_str:
+ cmdline_options_str = REVISION_SCRIPT_TOKEN + " " + cmdline_options_str
+ cmdline_options_list = shlex.split(cmdline_options_str)
+ cmdline_options_list = [
+ option.replace(REVISION_SCRIPT_TOKEN, path)
+ for option in cmdline_options_list
]
- return options_list
+ return cmdline_options_list
@register("console_scripts")
)
iter_ = pkg_resources.iter_entry_points("console_scripts", entrypoint_name)
impl = next(iter_)
- options_str = options.get("options", "")
- options_list = _parse_options(options_str, path)
+ cwd = options.get("cwd", None)
+ cmdline_options_str = options.get("options", "")
+ cmdline_options_list = _parse_cmdline_options(cmdline_options_str, path)
subprocess.run(
[
"import %s; %s()"
% (impl.module_name, ".".join((impl.module_name,) + impl.attrs)),
]
- + options_list
+ + cmdline_options_list,
+ cwd=cwd,
)
function, this configuration variable would refer to the name under
which the custom hook was registered; see the next section for an example.
-* ``entrypoint`` - this part of the configuration is specific to the
- ``"console_scripts"`` hook runner. This is the name of the `setuptools entrypoint <https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points>`_
+The following configuration options are specific to the ``"console_scripts"``
+hook runner:
+
+* ``entrypoint`` - the name of the `setuptools entrypoint <https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points>`_
that is used to define the console script. Within the scope of standard
Python console scripts, this name will match the name of the shell command
that is usually run for the code formatting tool, in this case ``black``.
-* ``options`` - this is also specific to the ``"console_scripts"`` hook runner.
- This is a line of command-line options that will be passed to the
- code formatting tool. In this case, we want to run the command
+* ``options`` - a line of command-line options that will be passed to
+ the code formatting tool. In this case, we want to run the command
``black /path/to/revision.py -l 79``. By default, the revision path is
positioned as the first argument. In order specify a different position,
we can use the ``REVISION_SCRIPT_FILENAME`` token as illustrated by the
autopep8.entrypoint = autopep8
autopep8.options = --in-place REVISION_SCRIPT_FILENAME
+* ``cwd`` - optional working directory from which the console script is run.
When running ``alembic revision -m "rev1"``, we will now see the ``black``
tool's output as well::
When using the above configuration, a newly generated revision file will
be processed first by the "black" tool, then by the "zimports" tool.
+Alternatively, one can run pre-commit itself as follows::
+
+ [post_write_hooks]
+
+ hooks = pre-commit
+
+ pre-commit.type = console_scripts
+ pre-commit.entrypoint = pre-commit
+ pre-commit.options = run --files REVISION_SCRIPT_FILENAME
+ pre-commit.cwd = %(here)s
+
+(The last line helps to ensure that the ``.pre-commit-config.yaml`` file
+will always be found, regardless of from where the hook was called.)
+
.. _post_write_hooks_custom:
Writing Custom Hooks as Python Functions
--- /dev/null
+.. change::
+ :tags: feature
+ :tickets: 822
+
+ Implement a ``.cwd`` (current working directory) suboption for post-write hooks
+ (of type ``console_scripts``). This is useful for tools like pre-commit, which
+ rely on the working directory to locate the necessary config files. Add
+ pre-commit as an example to the documentation. Minor change: rename some variables
+ from ticket #819 to improve readability.
)
def _run_black_with_config(
- self, input_config, expected_additional_arguments_fn
+ self, input_config, expected_additional_arguments_fn, cwd=None
):
self.cfg = _no_sql_testing_config(directives=input_config)
impl = mock.Mock(attrs=("foo", "bar"), module_name="black_module")
"-c",
"import black_module; black_module.foo.bar()",
]
- + expected_additional_arguments_fn(rev.path)
+ + expected_additional_arguments_fn(rev.path),
+ cwd=cwd,
)
],
)
self._run_black_with_config(
input_config, expected_additional_arguments_fn
)
+
+ def test_black_with_cwd(self):
+ input_config = """
+[post_write_hooks]
+hooks = black
+black.type = console_scripts
+black.entrypoint = black
+black.cwd = /path/to/cwd
+ """
+
+ def expected_additional_arguments_fn(rev_path):
+ return [rev_path]
+
+ self._run_black_with_config(
+ input_config, expected_additional_arguments_fn, cwd="/path/to/cwd"
+ )