Skip to content

Commit

Permalink
Accept path/to/file.py::function_name strings for events arguments (
Browse files Browse the repository at this point in the history
#214)

* - `events` kwargs now accepts path to functions inside files with `path/to/file.py::function` syntax
- Added `--event` CLI argument to `md2po` and `po2md` CLIs.

* Fix error in docstring
  • Loading branch information
mondeja committed Dec 18, 2021
1 parent 8c9a829 commit bbeb330
Show file tree
Hide file tree
Showing 13 changed files with 250 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.3.82
current_version = 0.3.83

[bumpversion:file:mdpo/__init__.py]

Expand Down
16 changes: 8 additions & 8 deletions docs/pre-commit-hooks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ so you don't need to specify them.
.. code-block:: yaml
- repo: https://github.com/mondeja/mdpo
rev: v0.3.82
rev: v0.3.83
hooks:
- id: md2po
args:
Expand All @@ -32,7 +32,7 @@ so you don't need to specify them.
.. code-block:: yaml
- repo: https://github.com/mondeja/mdpo
rev: v0.3.82
rev: v0.3.83
hooks:
- id: md2po
files: ^README\.md
Expand All @@ -53,7 +53,7 @@ po2md
.. code-block:: yaml
- repo: https://github.com/mondeja/mdpo
rev: v0.3.82
rev: v0.3.83
hooks:
- id: po2md
args:
Expand All @@ -68,7 +68,7 @@ po2md
.. code-block:: yaml
- repo: https://github.com/mondeja/mdpo
rev: v0.3.82
rev: v0.3.83
hooks:
- id: po2md
files: ^README\.md
Expand All @@ -91,7 +91,7 @@ md2po2md
.. code-block:: yaml
- repo: https://github.com/mondeja/mdpo
rev: v0.3.82
rev: v0.3.83
hooks:
- id: md2po2md
args:
Expand All @@ -108,7 +108,7 @@ md2po2md
.. code-block:: yaml
- repo: https://github.com/mondeja/mdpo
rev: v0.3.82
rev: v0.3.83
hooks:
- id: md2po2md
files: ^README\.md
Expand All @@ -133,7 +133,7 @@ mdpo2html
.. code-block:: yaml
- repo: https://github.com/mondeja/mdpo
rev: v0.3.82
rev: v0.3.83
hooks:
- id: mdpo2html
args:
Expand All @@ -148,7 +148,7 @@ mdpo2html
.. code-block:: yaml
- repo: https://github.com/mondeja/mdpo
rev: v0.3.82
rev: v0.3.83
hooks:
- id: mdpo2html
files: ^README\.html
Expand Down
2 changes: 1 addition & 1 deletion mdpo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

__description__ = ('Markdown files translation using PO files.')
__title__ = 'mdpo'
__version__ = '0.3.82'
__version__ = '0.3.83'
__all__ = [
'__description__',
'__title__',
Expand Down
33 changes: 33 additions & 0 deletions mdpo/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,36 @@ def add_pre_commit_option(parser):
help='Run in pre-commit mode, which returns code 1 at exit when a file'
' has been changed or previously did not exist.',
)


def add_event_argument(parser):
"""Add the ``--event`` optional argument to an argument parser.
Args:
parser (:py:class:`argparse.ArgumentParser`): Arguments parser to
extend.
"""
parser.add_argument(
'-e', '--event', dest='events', default=[], action='append',
metavar='event_name: path/to/file.py::function_name',
help='Custom events executed during the parser. They are used for'
' customize the output. See the documentation for available'
' event names. This argument can be passed multiple times.',
)


def parse_event_argument(value):
"""Parse ``--event`` CLI argument values.
Args:
value (list): Event names and function paths in the form
``event_name: path/to/file.py::func``.
Returns:
dict: Mapping of event names and `file::function` paths.
"""
events = {}
for event_name_filefunc in value:
event_name, filefunc = event_name_filefunc.split(':', maxsplit=1)
events[event_name.strip()] = filefunc.strip()
return events
66 changes: 66 additions & 0 deletions mdpo/event.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Custom events executed during the parsing process of an implementation."""

import importlib
import os
import sys


Expand Down Expand Up @@ -104,3 +106,67 @@ def print_link_reference(self, target, href, title):
'command': print_command,
'link_reference': print_link_reference,
}


def parse_events_kwarg(events_kwarg):
"""Parse ``events`` kwarg passed to implementations.
Each event can be a function or a path to a function inside a file
using the syntax ``path/to/file.py::function``.
Args:
events_kwarg (dict): Dictionary of event names and their location.
Returns:
dict: Event names mapping to a list of functions to execute.
"""
events = {}
for event_name, funcs_func_or_filefuncs in events_kwarg.items():
funcs_or_filefuncs = (
[funcs_func_or_filefuncs]
if callable(funcs_func_or_filefuncs)
or isinstance(funcs_func_or_filefuncs, str)
else funcs_func_or_filefuncs
)

events[event_name] = []
for func_or_filefunc in funcs_or_filefuncs:
if isinstance(func_or_filefunc, str):
# is a string with the syntax 'path/to/file.py::func'
#
# import the file, execute as a module and get the function
if '::' not in func_or_filefunc:
raise ValueError(
'Function not specified for file'
f" '{func_or_filefunc}' defined for event"
f" '{event_name}'",
)

fpath, funcname = func_or_filefunc.split('::')
if not os.path.isfile(fpath):
raise FileNotFoundError(
f"File '{fpath}' specified for event"
f" '{event_name}' not found",
)

modname = fpath.split('.')[0].replace(os.sep, '.')
if modname in sys.modules:
mod = sys.modules[modname]
else:
spec = importlib.util.spec_from_file_location(
modname, fpath,
)
mod = importlib.util.module_from_spec(spec)
sys.modules[modname] = mod
spec.loader.exec_module(mod)

if not hasattr(mod, funcname):
raise ValueError(
f"Function '{funcname}' specified for event"
f" '{event_name}' not found in file '{fpath}'",
)
events[event_name].append(getattr(mod, funcname))
else:
# is a function
events[event_name].append(func_or_filefunc)
return events
18 changes: 9 additions & 9 deletions mdpo/md2po/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
normalize_mdpo_command_aliases,
parse_mdpo_html_command,
)
from mdpo.event import debug_events, raise_skip_event
from mdpo.event import debug_events, parse_events_kwarg, raise_skip_event
from mdpo.io import (
filter_paths,
save_file_checking_file_changed,
Expand Down Expand Up @@ -152,12 +152,9 @@ def __init__(self, files_or_content, **kwargs):
'extensions',
DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS,
)
self.events = {}
if 'events' in kwargs:
for event_name, functions in kwargs['events'].items():
self.events[event_name] = (
[functions] if callable(functions) else functions
)
self.events = (
parse_events_kwarg(kwargs['events']) if 'events' in kwargs else {}
)
if kwargs.get('debug'):
for event_name, function in debug_events('md2po').items():
if event_name not in self.events:
Expand Down Expand Up @@ -1105,10 +1102,10 @@ def markdown_to_pofile(
file contains previous metadata fields, these will be updated
preserving the values of the already defined.
events (dict): Preprocessing events executed during the parsing
process. You can use these to customize the extraction process.
process that can be used to customize the extraction process.
Takes functions or list of functions as values. If one of these
functions returns ``False``, that part of the parsing is skipped
by md2po (usually a MD4C event). The available events are:
by ``md2po``. Available events are the next:
* ``enter_block(self, block, details)``: Executed when the parsing
a Markdown block starts.
Expand All @@ -1127,6 +1124,9 @@ def markdown_to_pofile(
* ``link_reference(self, target, href, title)``: Executed when a
link reference is going to be stored.
You can also define the location of these functions by strings
with the syntax ``path/to/file.py::function_name``.
All ``self`` arguments are an instance of Md2Po parser. You can
take advanced control of the parsing process manipulating the
state of the parser. For example, if you want to skip a certain
Expand Down
5 changes: 5 additions & 0 deletions mdpo/md2po/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
add_common_cli_first_arguments,
add_debug_option,
add_encoding_arguments,
add_event_argument,
add_extensions_argument,
add_nolocation_option,
add_pre_commit_option,
add_wrapwidth_argument,
cli_codespan,
parse_command_aliases_cli_arguments,
parse_event_argument,
parse_metadata_cli_arguments,
)
from mdpo.context import environ
Expand Down Expand Up @@ -149,6 +151,7 @@ def build_parser():
)

add_command_alias_argument(parser)
add_event_argument(parser)
add_debug_option(parser)
add_pre_commit_option(parser)
return parser
Expand Down Expand Up @@ -186,6 +189,7 @@ def parse_options(args=[]):
opts.command_aliases = parse_command_aliases_cli_arguments(
opts.command_aliases,
)
opts.events = parse_event_argument(opts.events)
opts.metadata = parse_metadata_cli_arguments(
opts.metadata,
)
Expand All @@ -209,6 +213,7 @@ def run(args=[]):
'ignore_msgids': opts.ignore_msgids,
'command_aliases': opts.command_aliases,
'metadata': opts.metadata,
'events': opts.events,
'debug': opts.debug,
'_check_saved_files_changed': opts.check_saved_files_changed,
}
Expand Down
26 changes: 13 additions & 13 deletions mdpo/po2md/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
normalize_mdpo_command_aliases,
parse_mdpo_html_command,
)
from mdpo.event import debug_events, raise_skip_event
from mdpo.event import debug_events, parse_events_kwarg, raise_skip_event
from mdpo.io import save_file_checking_file_changed, to_file_content_if_is_file
from mdpo.md import MarkdownSpanWrapper, parse_link_references
from mdpo.md4c import DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS
Expand Down Expand Up @@ -98,12 +98,9 @@ def __init__(self, pofiles, ignore=[], po_encoding=None, **kwargs):
DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS,
)

self.events = {}
if 'events' in kwargs:
for event_name, functions in kwargs['events'].items():
self.events[event_name] = (
[functions] if callable(functions) else functions
)
self.events = (
parse_events_kwarg(kwargs['events']) if 'events' in kwargs else {}
)
if kwargs.get('debug'):
for event_name, function in debug_events('po2md').items():
if event_name not in self.events:
Expand Down Expand Up @@ -971,15 +968,15 @@ def pofile_to_markdown(
parameter.
wrapwidth (int): Maximum width used rendering the Markdown output.
events (dict): Preprocessing events executed during the translation
process. You can use these to customize the output. Takes functions
are values. If one of these functions returns ``False``, that part
of the translation process is skipped by po2md. The available
events are:
process that can be used to customize the output. Takes list of
functions as values. If one of these functions returns ``False``,
that part of the translation process is skipped by ``po2md``.
Available events are the next:
* ``enter_block(self, block, details)``: Executed when the parsing
a Markdown block starts.
of a Markdown block starts.
* ``leave_block(self, block, details)``: Executed when the parsing
a Markdown block ends.
of a Markdown block ends.
* ``enter_span(self, span, details)``: Executed when the parsing of
a Markdown span starts.
* ``leave_span(self, span, details)``: Executed when the parsing of
Expand All @@ -993,6 +990,9 @@ def pofile_to_markdown(
* ``link_reference(self, target, href, title)``: Executed when each
reference link is being written in the output (at the end of the
translation process).
You can also define the location of these functions by strings
with the syntax ``path/to/file.py::function_name``.
debug (bool): Add events displaying all parsed elements in the
translation process.
Expand Down
5 changes: 5 additions & 0 deletions mdpo/po2md/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
add_common_cli_first_arguments,
add_debug_option,
add_encoding_arguments,
add_event_argument,
add_pre_commit_option,
add_wrapwidth_argument,
cli_codespan,
parse_command_aliases_cli_arguments,
parse_event_argument,
)
from mdpo.context import environ
from mdpo.po2md import Po2Md
Expand Down Expand Up @@ -66,6 +68,7 @@ def build_parser():
add_wrapwidth_argument(parser, markup='md', default='80')
add_encoding_arguments(parser)
add_command_alias_argument(parser)
add_event_argument(parser)
add_debug_option(parser)
add_pre_commit_option(parser)
return parser
Expand Down Expand Up @@ -94,6 +97,7 @@ def parse_options(args):
opts.command_aliases = parse_command_aliases_cli_arguments(
opts.command_aliases,
)
opts.events = parse_event_argument(opts.events)

opts.pofiles = set(itertools.chain(*opts.pofiles)) # flatten

Expand All @@ -110,6 +114,7 @@ def run(args=[]):
po_encoding=opts.po_encoding,
command_aliases=opts.command_aliases,
wrapwidth=opts.wrapwidth,
events=opts.events,
debug=opts.debug,
_check_saved_files_changed=opts.check_saved_files_changed,
)
Expand Down
6 changes: 3 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = mdpo
version = 0.3.82
version = 0.3.83
description = Markdown files translation using PO files.
long_description = file: README.md
long_description_content_type = text/markdown
Expand Down Expand Up @@ -135,7 +135,7 @@ sections = STDLIB,THIRDPARTY,FIRSTPARTY,TESTS,LOCALFOLDER

[build_sphinx]
project = mdpo
version = 0.3.82
release = 0.3.82
version = 0.3.83
release = 0.3.83
source-dir = docs
build-dir = docs/_build

0 comments on commit bbeb330

Please sign in to comment.