From aeae7166ea09c3741ceb527fcd31ffe40a1cefb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar?= Date: Sat, 18 Dec 2021 22:09:40 +0100 Subject: [PATCH] Make public some state properties of parsers and document them. (#215) * Make public some state properties of parsers and document them. * Refactor variable name * Bump version --- .bumpversion.cfg | 2 +- .gitignore | 1 + docs/commands.rst | 3 +- .../devref/mdpo/md2po/mdpo.md2po.__init__.rst | 2 - .../devref/mdpo/po2md/mdpo.po2md.__init__.rst | 2 - docs/pre-commit-hooks.rst | 16 +- mdpo/__init__.py | 2 +- mdpo/event.py | 14 + mdpo/md.py | 82 ++--- mdpo/md2po/__init__.py | 294 +++++++++------- mdpo/md4c.py | 4 +- mdpo/mdpo2html/__init__.py | 34 +- mdpo/po2md/__init__.py | 321 +++++++++++------- setup.cfg | 6 +- test/conftest.py | 18 +- test/test_unit/test_md2po/test_md2po_cli.py | 2 +- test/test_unit/test_po2md/test_po2md_cli.py | 2 +- .../test_unit/test_po2md/test_po2md_events.py | 2 +- 18 files changed, 473 insertions(+), 334 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 09c8d0f2..b605ea6d 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.3.83 +current_version = 0.3.84 [bumpversion:file:mdpo/__init__.py] diff --git a/.gitignore b/.gitignore index 1329aec1..876389c4 100644 --- a/.gitignore +++ b/.gitignore @@ -120,6 +120,7 @@ venv/ ENV/ env.bak/ venv.bak/ +.vscode # Spyder project settings .spyderproject diff --git a/docs/commands.rst b/docs/commands.rst index b7357610..878ab08f 100644 --- a/docs/commands.rst +++ b/docs/commands.rst @@ -143,7 +143,8 @@ the next commands: * ````: Include all codeblocks placed after this command (same behaviour as passing the argument :ref:`md2po---include-codeblocks` or ``include_codeblocks=True`` if you are - using the :ref:`programmatic interface`.). + using the + :doc:`programmatic interface `). * ````: Does not include codeblocks placed after this command. * ````: Include next codeblock placed after this diff --git a/docs/devref/mdpo/md2po/mdpo.md2po.__init__.rst b/docs/devref/mdpo/md2po/mdpo.md2po.__init__.rst index e8ab93bf..dc44fd5c 100644 --- a/docs/devref/mdpo/md2po/mdpo.md2po.__init__.rst +++ b/docs/devref/mdpo/md2po/mdpo.md2po.__init__.rst @@ -2,7 +2,5 @@ __init__.py *********** -.. _md2po-init: - .. automodule:: mdpo.md2po.__init__ :members: diff --git a/docs/devref/mdpo/po2md/mdpo.po2md.__init__.rst b/docs/devref/mdpo/po2md/mdpo.po2md.__init__.rst index 059dd47c..fe758936 100644 --- a/docs/devref/mdpo/po2md/mdpo.po2md.__init__.rst +++ b/docs/devref/mdpo/po2md/mdpo.po2md.__init__.rst @@ -2,7 +2,5 @@ __init__.py *********** -.. _po2md-init: - .. automodule:: mdpo.po2md.__init__ :members: diff --git a/docs/pre-commit-hooks.rst b/docs/pre-commit-hooks.rst index 17cef23e..4bc970fe 100644 --- a/docs/pre-commit-hooks.rst +++ b/docs/pre-commit-hooks.rst @@ -19,7 +19,7 @@ so you don't need to specify them. .. code-block:: yaml - repo: https://github.com/mondeja/mdpo - rev: v0.3.83 + rev: v0.3.84 hooks: - id: md2po args: @@ -32,7 +32,7 @@ so you don't need to specify them. .. code-block:: yaml - repo: https://github.com/mondeja/mdpo - rev: v0.3.83 + rev: v0.3.84 hooks: - id: md2po files: ^README\.md @@ -53,7 +53,7 @@ po2md .. code-block:: yaml - repo: https://github.com/mondeja/mdpo - rev: v0.3.83 + rev: v0.3.84 hooks: - id: po2md args: @@ -68,7 +68,7 @@ po2md .. code-block:: yaml - repo: https://github.com/mondeja/mdpo - rev: v0.3.83 + rev: v0.3.84 hooks: - id: po2md files: ^README\.md @@ -91,7 +91,7 @@ md2po2md .. code-block:: yaml - repo: https://github.com/mondeja/mdpo - rev: v0.3.83 + rev: v0.3.84 hooks: - id: md2po2md args: @@ -108,7 +108,7 @@ md2po2md .. code-block:: yaml - repo: https://github.com/mondeja/mdpo - rev: v0.3.83 + rev: v0.3.84 hooks: - id: md2po2md files: ^README\.md @@ -133,7 +133,7 @@ mdpo2html .. code-block:: yaml - repo: https://github.com/mondeja/mdpo - rev: v0.3.83 + rev: v0.3.84 hooks: - id: mdpo2html args: @@ -148,7 +148,7 @@ mdpo2html .. code-block:: yaml - repo: https://github.com/mondeja/mdpo - rev: v0.3.83 + rev: v0.3.84 hooks: - id: mdpo2html files: ^README\.html diff --git a/mdpo/__init__.py b/mdpo/__init__.py index dc8c568a..28f31f19 100644 --- a/mdpo/__init__.py +++ b/mdpo/__init__.py @@ -2,7 +2,7 @@ __description__ = ('Markdown files translation using PO files.') __title__ = 'mdpo' -__version__ = '0.3.83' +__version__ = '0.3.84' __all__ = [ '__description__', '__title__', diff --git a/mdpo/event.py b/mdpo/event.py index 20edf170..f2100c61 100644 --- a/mdpo/event.py +++ b/mdpo/event.py @@ -108,6 +108,20 @@ def print_link_reference(self, target, href, title): } +def add_debug_events(implementation_name, events): + """Add debugging events to an events dict. + + Args: + implementation_name (str): Implementation name, shown when + printing debugging events. + events (dict): Events dictionary. + """ + for event_name, function in debug_events(implementation_name).items(): + if event_name not in events: + events[event_name] = [] + events[event_name].append(function) + + def parse_events_kwarg(events_kwarg): """Parse ``events`` kwarg passed to implementations. diff --git a/mdpo/md.py b/mdpo/md.py index fbf3f736..de96fc5d 100644 --- a/mdpo/md.py +++ b/mdpo/md.py @@ -60,7 +60,7 @@ class MarkdownSpanWrapper: # state 'output', - '_current_line', + 'current_line', '_current_aspan_href', '_current_aspan_title', '_inside_codespan', @@ -83,7 +83,7 @@ def __init__( self.md4c_extensions = md4c_extensions self.output = '' - self._current_line = '' + self.current_line = '' self.bold_start_string = kwargs.get('bold_start_string', '**') self.bold_end_string = kwargs.get('bold_end_string', '**') @@ -131,78 +131,78 @@ def leave_block(self, block, details): def enter_span(self, span, details): if span is md4c.SpanType.CODE: self._inside_codespan = True - self._current_line += self.code_start_string + self.current_line += self.code_start_string elif span is md4c.SpanType.A: - self._current_line += '[' + self.current_line += '[' self._current_aspan_href = details['href'][0][1] self._current_aspan_title = ( details['title'][0][1] if details['title'] else None ) elif span is md4c.SpanType.STRONG: - self._current_line += self.bold_start_string + self.current_line += self.bold_start_string elif span is md4c.SpanType.EM: - self._current_line += self.italic_start_string + self.current_line += self.italic_start_string elif span is md4c.SpanType.WIKILINK: - self._current_line += self.wikilink_start_string + self.current_line += self.wikilink_start_string self._current_wikilink_target = details['target'][0][1] elif span is md4c.SpanType.IMG: - self._current_line += '![' + self.current_line += '![' def leave_span(self, span, details): if span is md4c.SpanType.CODE: self._inside_codespan = False - self._current_line += self.code_end_string + self.current_line += self.code_end_string elif span is md4c.SpanType.A: - if self._current_line[-1] != '>': - self._current_line += f']({self._current_aspan_href}' + if self.current_line[-1] != '>': + self.current_line += f']({self._current_aspan_href}' if self._current_aspan_title: - self._current_line += ( + self.current_line += ( f' "{polib.escape(self._current_aspan_title)}"' ) - self._current_line += ')' + self.current_line += ')' self._current_aspan_href = False self._current_aspan_href = None self._current_aspan_title = None elif span is md4c.SpanType.STRONG: - self._current_line += self.bold_end_string + self.current_line += self.bold_end_string elif span is md4c.SpanType.EM: - self._current_line += self.italic_end_string + self.current_line += self.italic_end_string elif span is md4c.SpanType.WIKILINK: - self._current_line += self.wikilink_end_string + self.current_line += self.wikilink_end_string self._current_wikilink_target = None elif span is md4c.SpanType.IMG: src = details['src'][0][1] - self._current_line += f']({src}' + self.current_line += f']({src}' if details['title']: title = details['title'][0][1] - self._current_line += f' "{polib.escape(title)}"' - self._current_line += ')' + self.current_line += f' "{polib.escape(title)}"' + self.current_line += ')' def text(self, block, text): if self._inside_codespan: width = self._get_currently_applied_width() indent = self._get_currently_applied_indent() - if len(self._current_line) + len(text) + 1 > width: - self._current_line = self._current_line.rstrip('`').rstrip(' ') - self.output += f'{indent}{self._current_line}\n' - self._current_line = '`' + if len(self.current_line) + len(text) + 1 > width: + self.current_line = self.current_line.rstrip('`').rstrip(' ') + self.output += f'{indent}{self.current_line}\n' + self.current_line = '`' n_backticks = min_not_max_chars_in_a_row( self.code_start_string[0], text, ) - 1 if n_backticks: - self._current_line += n_backticks * '`' + self.current_line += n_backticks * '`' - self._current_line += f'{text}{n_backticks * "`"}' + self.current_line += f'{text}{n_backticks * "`"}' elif self._current_wikilink_target: if text != self._current_wikilink_target: - self._current_line += ( + self.current_line += ( f'{self._current_wikilink_target}|{text}' ) else: - self._current_line += text + self.current_line += text return else: if self._current_aspan_href: @@ -210,8 +210,8 @@ def text(self, block, text): self._current_aspan_href == text and not self._current_aspan_title ): - self._current_line = ( - f"{self._current_line.rstrip(' [')} <{text}>" + self.current_line = ( + f"{self.current_line.rstrip(' [')} <{text}>" ) return @@ -227,29 +227,29 @@ def text(self, block, text): text_splits = text.split(' ') width = self._get_currently_applied_width() if self._current_aspan_href: # links wrapping - if len(self._current_line) + len(text_splits[0]) + 1 > width: + if len(self.current_line) + len(text_splits[0]) + 1 > width: indent = self._get_currently_applied_indent() # new link text in newline - self._current_line = self._current_line[:-1].rstrip(' ') - self.output += f'{indent}{self._current_line}\n' - self._current_line = '[' + self.current_line = self.current_line[:-1].rstrip(' ') + self.output += f'{indent}{self.current_line}\n' + self.current_line = '[' width *= .95 # latest word in newline for i, text_split in enumerate(text_splits): # +1 is a space here - if len(self._current_line) + len(text_split) + 1 > width: + if len(self.current_line) + len(text_split) + 1 > width: if i or ( - self._current_line and self._current_line[-1] == ' ' + self.current_line and self.current_line[-1] == ' ' ): indent = self._get_currently_applied_indent() - self.output += f'{indent}{self._current_line}\n' - self._current_line = '' + self.output += f'{indent}{self.current_line}\n' + self.current_line = '' width = self._get_currently_applied_width() if self._current_aspan_href: width *= .95 elif i: - self._current_line += ' ' - self._current_line += text_split + self.current_line += ' ' + self.current_line += text_split def wrap(self, text): """Wraps reasonably Markdown lines.""" @@ -266,9 +266,9 @@ def wrap(self, text): self.text, ) - if self._current_line: + if self.current_line: self.output += ( - f'{self._get_currently_applied_indent()}{self._current_line}' + f'{self._get_currently_applied_indent()}{self.current_line}' ) if self.first_line_width == self.width: # is not blockquote nor list self.output += '\n' diff --git a/mdpo/md2po/__init__.py b/mdpo/md2po/__init__.py index 9f9d5e77..26e96616 100644 --- a/mdpo/md2po/__init__.py +++ b/mdpo/md2po/__init__.py @@ -10,7 +10,7 @@ normalize_mdpo_command_aliases, parse_mdpo_html_command, ) -from mdpo.event import debug_events, parse_events_kwarg, raise_skip_event +from mdpo.event import add_debug_events, parse_events_kwarg, raise_skip_event from mdpo.io import ( filter_paths, save_file_checking_file_changed, @@ -31,6 +31,30 @@ class Md2Po: + """Markdown to PO files extractor. + + This class is where all the extraction process is carried out. + If you are executing custom extraction events, you may want to + read the documentation about the properties of this class to + properly control the internal state of the parser. + + Example: + If you want to extract all "Foo" messages as "Bar", + regardless of the content of the Markdown input, + you could do something like: + + .. code-block:: python + + def transform_foo(self, block, text): + if text == 'Foo': + self.current_msgid = 'Bar' # self is Md2Po + return False + + markdown_to_pofile('Foo', events={'text': transform_foo}) + + The public internal properties of this class are documented + below: + """ __slots__ = { 'filepaths', 'content', @@ -54,15 +78,17 @@ class Md2Po: '_current_top_level_block_type', '_current_markdown_filepath', - '_current_msgid', - '_current_tcomment', - '_current_msgctxt', + # Public class properties + 'current_msgid', + 'current_tcomment', + 'current_msgctxt', + 'link_references', + 'disable', + 'disable_next_block', + 'enable_next_block', + 'include_next_codeblock', + 'disable_next_codeblock', - '_disable', - '_disable_next_block', - '_enable_next_block', - '_include_next_codeblock', - '_disable_next_codeblock', '_saved_files_changed', '_enterspan_replacer', @@ -106,7 +132,6 @@ class Md2Po: '_current_wikilink_target', '_current_imgspan', '_uls_deep', - '_link_references', } def __init__(self, files_or_content, **kwargs): @@ -122,55 +147,92 @@ def __init__(self, files_or_content, **kwargs): else: self.content = files_or_content + #: :py:class:`polib.POFile` PO file object representing + #: the extracted content. self.pofile = None + + #: str: PO file path to which the content will be extracted. self.po_filepath = None - self.msgstr = kwargs.get('msgstr', '') - self.found_entries = [] - self.disabled_entries = [] - self._current_msgid = '' - self._current_tcomment = None - self._current_msgctxt = None - self.ignore_msgids = kwargs.get('ignore_msgids', []) - self.command_aliases = normalize_mdpo_command_aliases( - kwargs.get('command_aliases', {}), - ) + #: str: Default msgstr used if the current is not found + #: inside the previous content of the specified PO file. + self.msgstr = kwargs.get('msgstr', '') - self.mark_not_found_as_obsolete = kwargs.get( - 'mark_not_found_as_obsolete', True, - ) - self.preserve_not_found = kwargs.get('preserve_not_found', True) + #: list: Extracted entries. + self.found_entries = [] - self.location = kwargs.get('location', True) - # "top level" here because blocks inside blocks are not taken into - # account for locations - self._current_top_level_block_number = 0 - self._current_top_level_block_type = None - self._current_markdown_filepath = None + #: list: Not extracted entries because the extractor + #: has been disabled while processing them. + self.disabled_entries = [] + #: list(str): MD4C extensions used to parse the content. + #: See all available in :doc:`/devref/mdpo/mdpo.md4c`. self.extensions = kwargs.get( 'extensions', DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS, ) + + #: dict: Custom events excuted during the parsing while + #: extracting content. 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: - self.events[event_name] = [] - self.events[event_name].append(function) + add_debug_events('md2po', self.events) - self.plaintext = kwargs.get('plaintext', False) + #: str: The msgid being currently built for the next + #: message entry. Keep in mind that, if you are executing + #: an event that will be followed by an span one + #: (``enter_span`` or ``exit_span``), the content of the + #: msgid will change before save it. + self.current_msgid = '' + + #: str: Translator comment that will be saved in the next + #: message. + self.current_tcomment = None + + #: str: Context message that will be saved in the next + #: message. + self.current_msgctxt = None + + #: bool: Indicates if the extractor is currently disabled, + #: which happens after a ```` command + #: is found, before any subsecuents ```` + #: commands. + self.disable = False + + #: bool: Indicates if the next block will be extracted. + self.disable_next_block = False + + #: bool: Indicates if the next block will be extracted + #: when the extractor is disabled (``disable is True``). + self.enable_next_block = False + #: bool: Indicates if the next codeblock will be extracted + #: when `include_codeblocks` is enabled. + self.include_next_codeblock = False + + #: bool: Indicates if the next codeblock will be extracted + #: when `include_codeblocks` is disabled. + self.disable_next_codeblock = False + + #: bool: Extract code blocks self.include_codeblocks = kwargs.get('include_codeblocks', False) - self._disable = False - self._disable_next_block = False - self._enable_next_block = False + #: The msgids to ignore for extraction + self.ignore_msgids = kwargs.get('ignore_msgids', []) + + self.command_aliases = ( + normalize_mdpo_command_aliases(kwargs['command_aliases']) + if 'command_aliases' in kwargs else {} + ) + + self.mark_not_found_as_obsolete = kwargs.get( + 'mark_not_found_as_obsolete', True, + ) + self.preserve_not_found = kwargs.get('preserve_not_found', True) - self._include_next_codeblock = False - self._disable_next_codeblock = False + self.plaintext = kwargs.get('plaintext', False) self._saved_files_changed = ( False if kwargs.get('_check_saved_files_changed') else None @@ -178,6 +240,13 @@ def __init__(self, files_or_content, **kwargs): self.metadata = {} + self.location = kwargs.get('location', True) + # "top level" here because blocks inside blocks are not taken into + # account for locations + self._current_top_level_block_number = 0 + self._current_top_level_block_type = None + self._current_markdown_filepath = None + if not self.plaintext: self.bold_start_string = kwargs.get('bold_start_string', '**') self.bold_end_string = kwargs.get('bold_end_string', '**') @@ -350,7 +419,7 @@ def __init__(self, files_or_content, **kwargs): # extracted without using MD4C, so we can preserve it as referenced self._current_aspan_ref_target = None - self._link_references = None + self.link_references = None self._current_wikilink_target = None self._current_imgspan = {} @@ -425,39 +494,39 @@ def _save_current_msgid(self, msgstr='', fuzzy=False): self.events, 'msgid', self, - self._current_msgid, + self.current_msgid, msgstr, - self._current_msgctxt, - self._current_tcomment, + self.current_msgctxt, + self.current_tcomment, ['fuzzy'] if fuzzy else [], ): return - if self._current_msgid: - if (not self._disable_next_block and not self._disable) or \ - self._enable_next_block: + if self.current_msgid: + if (not self.disable_next_block and not self.disable) or \ + self.enable_next_block: self._save_msgid( - self._current_msgid, + self.current_msgid, msgstr=msgstr or self.msgstr, - msgctxt=self._current_msgctxt, - tcomment=self._current_tcomment, + msgctxt=self.current_msgctxt, + tcomment=self.current_tcomment, fuzzy=fuzzy, ) else: self.disabled_entries.append( polib.POEntry( - msgid=self._current_msgid, + msgid=self.current_msgid, msgstr=msgstr or self.msgstr, - msgctxt=self._current_msgctxt, - tcomment=self._current_tcomment, + msgctxt=self.current_msgctxt, + tcomment=self.current_tcomment, flags=['fuzzy'] if fuzzy else [], ), ) - self._disable_next_block = False - self._enable_next_block = False - self._current_msgid = '' - self._current_tcomment = None - self._current_msgctxt = None + self.disable_next_block = False + self.enable_next_block = False + self.current_msgid = '' + self.current_tcomment = None + self.current_msgctxt = None def command(self, mdpo_command, comment, original_command): # raise 'command' event @@ -475,20 +544,20 @@ def command(self, mdpo_command, comment, original_command): 'mdpo-disable-next-block', 'mdpo-disable-next-line', ): - self._disable_next_block = True + self.disable_next_block = True elif mdpo_command == 'mdpo-disable': - self._disable = True + self.disable = True elif mdpo_command == 'mdpo-enable': - self._disable = False + self.disable = False elif mdpo_command in ( 'mdpo-enable-next-block', 'mdpo-enable-next-line', ): - self._enable_next_block = True + self.enable_next_block = True elif mdpo_command == 'mdpo-include-codeblock': - self._include_next_codeblock = True + self.include_next_codeblock = True elif mdpo_command == 'mdpo-disable-codeblock': - self._disable_next_codeblock = True + self.disable_next_codeblock = True elif mdpo_command == 'mdpo-disable-codeblocks': self.include_codeblocks = False elif mdpo_command == 'mdpo-include-codeblocks': @@ -500,14 +569,14 @@ def command(self, mdpo_command, comment, original_command): ' extracted comment with the command' f' \'{original_command}\'.', ) - self._current_tcomment = comment + self.current_tcomment = comment elif mdpo_command == 'mdpo-context': if not comment: raise ValueError( 'You need to specify a string for the' f' context with the command \'{original_command}\'.', ) - self._current_msgctxt = comment + self.current_msgctxt = comment elif mdpo_command == 'mdpo-include': if not comment: raise ValueError( @@ -515,7 +584,7 @@ def command(self, mdpo_command, comment, original_command): ' comment to include with the command' f' \'{original_command}\'.', ) - self._current_msgid = comment + self.current_msgid = comment self._save_current_msgid() def _process_command(self, text): @@ -621,11 +690,11 @@ def leave_block(self, block, details): if block is md4c.BlockType.CODE: self._inside_codeblock = False - if not self._disable_next_codeblock: - if self.include_codeblocks or self._include_next_codeblock: + if not self.disable_next_codeblock: + if self.include_codeblocks or self.include_next_codeblock: self._save_current_msgid() - self._include_next_codeblock = False - self._disable_next_codeblock = False + self.include_next_codeblock = False + self.disable_next_codeblock = False elif block is md4c.BlockType.HTML: self._inside_htmlblock = False else: @@ -668,7 +737,7 @@ def not_plaintext_enter_span(self, span, details): pass else: try: - self._current_msgid += ( + self.current_msgid += ( self._enterspan_replacer[span.value] ) except KeyError: @@ -677,8 +746,8 @@ def not_plaintext_enter_span(self, span, details): if span is md4c.SpanType.A: # here resides the logic of discover if the current link # is referenced - if self._link_references is None: - self._link_references = parse_link_references(self.content) + if self.link_references is None: + self.link_references = parse_link_references(self.content) self._inside_aspan = True @@ -687,7 +756,7 @@ def not_plaintext_enter_span(self, span, details): if details['title']: current_aspan_title = details['title'][0][1] - for target, href, title in self._link_references: + for target, href, title in self.link_references: if ( href == current_aspan_href and title == current_aspan_title @@ -695,7 +764,7 @@ def not_plaintext_enter_span(self, span, details): self._current_aspan_ref_target = target break else: - for target, href, _ in self._link_references: + for target, href, _ in self.link_references: if href == current_aspan_href: self._current_aspan_ref_target = target break @@ -707,10 +776,10 @@ def not_plaintext_enter_span(self, span, details): # will be escaped # # save the index char of the opening backtick - self._codespan_start_index = len(self._current_msgid) - 1 + self._codespan_start_index = len(self.current_msgid) - 1 elif span is md4c.SpanType.IMG: - if self._link_references is None: - self._link_references = parse_link_references(self.content) + if self.link_references is None: + self.link_references = parse_link_references(self.content) self._current_imgspan['src'] = details['src'][0][1] self._current_imgspan['title'] = '' if not details['title'] \ @@ -733,7 +802,7 @@ def not_plaintext_leave_span(self, span, details): if not self._inside_uspan: if span is md4c.SpanType.WIKILINK: - self._current_msgid += self._current_wikilink_target + self.current_msgid += self._current_wikilink_target self._current_wikilink_target = None if self._inside_aspan: # span inside link text try: @@ -744,7 +813,7 @@ def not_plaintext_leave_span(self, span, details): pass else: try: - self._current_msgid += ( + self.current_msgid += ( self._leavespan_replacer[span.value] ) except KeyError: @@ -752,9 +821,9 @@ def not_plaintext_leave_span(self, span, details): if span is md4c.SpanType.A: if self._current_aspan_ref_target: # referenced link - self._current_msgid += f'[{self._current_aspan_text}]' + self.current_msgid += f'[{self._current_aspan_text}]' if self._current_aspan_ref_target != self._current_aspan_text: - self._current_msgid += ( + self.current_msgid += ( f'[{self._current_aspan_ref_target}]' ) self._current_aspan_ref_target = None @@ -762,14 +831,14 @@ def not_plaintext_leave_span(self, span, details): title = details['title'][0][1] if details['title'] else '' if self._current_aspan_text == details['href'][0][1]: # autolink vs link clash (see implementation notes) - self._current_msgid += f'<{self._current_aspan_text}' + self.current_msgid += f'<{self._current_aspan_text}' if title: - self._current_msgid += f' "{polib.escape(title)}"' - self._current_msgid += '>' + self.current_msgid += f' "{polib.escape(title)}"' + self.current_msgid += '>' else: title_part = f' "{polib.escape(title)}"' if title else '' href = details['href'][0][1] - self._current_msgid += ( + self.current_msgid += ( f'[{self._current_aspan_text}]({href}{title_part})' ) self._inside_aspan = False @@ -784,7 +853,7 @@ def not_plaintext_leave_span(self, span, details): self._codespan_backticks * self.code_end_string ) else: - self._current_msgid += ( + self.current_msgid += ( self._codespan_backticks * self.code_end_string ) self._codespan_backticks = None @@ -793,12 +862,12 @@ def not_plaintext_leave_span(self, span, details): imgspan_src = details['src'][0][1] if details['title']: imgspan_title = details['title'][0][1] - for target, href, title in self._link_references: + for target, href, title in self.link_references: if href == imgspan_src and title == imgspan_title: referenced_target = target break else: - for target, href, _ in self._link_references: + for target, href, _ in self.link_references: if href == imgspan_src: referenced_target = target break @@ -818,7 +887,7 @@ def not_plaintext_leave_span(self, span, details): if self._inside_aspan: self._current_aspan_text += img_markup else: - self._current_msgid += img_markup + self.current_msgid += img_markup elif span is md4c.SpanType.U: self._inside_uspan = False @@ -844,10 +913,10 @@ def text(self, block, text): self.code_start_string, text, ) - 1 - self._current_msgid = '{}{}{}'.format( - self._current_msgid[:self._codespan_start_index], + self.current_msgid = '{}{}{}'.format( + self.current_msgid[:self._codespan_start_index], self._codespan_backticks * self.code_start_string, - self._current_msgid[self._codespan_start_index:], + self.current_msgid[self._codespan_start_index:], ) if self._inside_aspan: self._current_aspan_text += text @@ -872,23 +941,23 @@ def text(self, block, text): f'{self._current_wikilink_target}|{text}' ) return - self._current_msgid += text + self.current_msgid += text else: - if not self._disable_next_codeblock: - if self.include_codeblocks or self._include_next_codeblock: - self._current_msgid += text + if not self.disable_next_codeblock: + if self.include_codeblocks or self.include_next_codeblock: + self.current_msgid += text else: self._process_command(text) def _dump_link_references(self): - if self._link_references: - self._disable_next_block = False - self._disable = False + if self.link_references: + self.disable_next_block = False + self.disable = False # 'link_reference' event pre_events = self.events.get('link_reference') - for target, href, title in self._link_references: + for target, href, title in self.link_references: if pre_events: skip = False for event in pre_events: @@ -897,13 +966,13 @@ def _dump_link_references(self): if skip: continue - self._current_msgid = '[{}]:{}{}'.format( + self.current_msgid = '[{}]:{}{}'.format( target, f' {href}' if href else '', f' "{title}"' if title else '', ) self._save_current_msgid( - msgstr=self._current_msgid, + msgstr=self.current_msgid, fuzzy=True, ) @@ -976,12 +1045,12 @@ def _parse(content): _parse(self.content) # reset state - self._disable_next_block = False - self._disable = False - self._enable_next_block = False - self._include_next_codeblock = False - self._disable_next_codeblock = False - self._link_references = None + self.disable_next_block = False + self.disable = False + self.enable_next_block = False + self.include_next_codeblock = False + self.disable_next_codeblock = False + self.link_references = None self._current_top_level_block_number = 0 self._current_top_level_block_type = None @@ -1136,7 +1205,7 @@ def markdown_to_pofile( def msgid_event(self, msgid, *args): if msgid == 'foo': - self._disable_next_block = True + self.disable_next_block = True debug (bool): Add events displaying all parsed elements in the extraction process. @@ -1154,9 +1223,6 @@ def msgid_event(self, msgid, *args): Returns: :class:`polib.POFile` Pofile instance with new msgids included. - - Raises - ValueError: when ``po_filepath`` is ``None`` and ``save`` is ``True``. """ return Md2Po( files_or_content, diff --git a/mdpo/md4c.py b/mdpo/md4c.py index 2ca65270..52522387 100644 --- a/mdpo/md4c.py +++ b/mdpo/md4c.py @@ -2,8 +2,8 @@ #: :list: `md4c parser `_ extensions used as -#: default by :ref:`md2po` and :ref:`po2md` -#: implementations. +#: default by :doc:`md2po ` and +#: :doc:`po2md ` implementations. DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS = [ 'collapse_whitespace', 'tables', diff --git a/mdpo/mdpo2html/__init__.py b/mdpo/mdpo2html/__init__.py index 7ef8f515..ab231a19 100644 --- a/mdpo/mdpo2html/__init__.py +++ b/mdpo/mdpo2html/__init__.py @@ -71,14 +71,14 @@ def __init__( self.replacer = [] self._raw_replacement = '' self.context = [] - self._current_msgctxt = None + self.current_msgctxt = None self.translations = None self.translations_with_msgctxt = None - self._disable = False - self._disable_next_block = False - self._enable_next_block = False + self.disable = False + self.disable_next_block = False + self.enable_next_block = False self.disabled_entries = [] # custom mdpo command resolution @@ -250,21 +250,21 @@ def _process_replacer(self): _current_replacement = html.unescape(_current_replacement) - if (self._disable and not self._enable_next_block) \ - or self._disable_next_block: + if (self.disable and not self.enable_next_block) \ + or self.disable_next_block: replacement = _current_replacement self.disabled_entries.append( polib.POEntry( msgid=replacement, msgstr='', - msgctxt=self._current_msgctxt, + msgctxt=self.current_msgctxt, ), ) else: - if self._current_msgctxt: + if self.current_msgctxt: replacement = self.translations_with_msgctxt[ - self._current_msgctxt + self.current_msgctxt ].get(_current_replacement) else: replacement = self.translations.get(_current_replacement) @@ -312,9 +312,9 @@ def _process_replacer(self): self.output += html_template self.context = [] - self._disable_next_block = False - self._enable_next_block = False - self._current_msgctxt = None + self.disable_next_block = False + self.enable_next_block = False + self.current_msgctxt = None # print('________________________________________________') @@ -414,18 +414,18 @@ def handle_comment(self, data): 'mdpo-disable-next-block', 'mdpo-disable-next-line', ): - self._disable_next_block = True + self.disable_next_block = True elif command == 'mdpo-disable': - self._disable = True + self.disable = True elif command == 'mdpo-enable': - self._disable = False + self.disable = False elif command in ( 'mdpo-enable-next-block', 'mdpo-enable-next-line', ): - self._enable_next_block = True + self.enable_next_block = True elif command == 'mdpo-context' and comment: - self._current_msgctxt = comment + self.current_msgctxt = comment elif command == 'mdpo-include-codeblock': warnings.warn( 'Code blocks translations are not supported' diff --git a/mdpo/po2md/__init__.py b/mdpo/po2md/__init__.py index f588f503..06344133 100644 --- a/mdpo/po2md/__init__.py +++ b/mdpo/po2md/__init__.py @@ -7,7 +7,7 @@ normalize_mdpo_command_aliases, parse_mdpo_html_command, ) -from mdpo.event import debug_events, parse_events_kwarg, raise_skip_event +from mdpo.event import add_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 @@ -20,6 +20,34 @@ class Po2Md: + """PO files to Markdown translator implementation. + + This class is where all the translation process is carried out. + If you are executing custom translation events, you may want to + read the documentation about the properties of this class to + properly control the internal state of the parser. + + Example: + If you want to translate all "Foo" messages as "Bar", + regardless of the content of the actual translation, + you could do something like: + + .. code-block:: python + + def transform_foo(self, block, text): + if text == 'Foo': + self.current_msgid = 'Bar' # self is Po2Md + return False + + pofile_to_markdown( + 'Foo', + pofile_path, + events={'text': transform_foo}, + ) + + The public internal properties of this class are documented + below: + """ __slots__ = { 'pofiles', 'output', @@ -46,20 +74,21 @@ class Po2Md: 'wikilink_start_string', 'wikilink_end_string', - # internal config - '_current_msgid', - '_current_msgctxt', - '_current_tcomment', - '_current_line', - '_outputlines', - '_disable_next_block', - '_disable', - '_enable_next_block', + # Public class properties + 'current_msgid', + 'current_tcomment', + 'current_msgctxt', + 'disable', + 'disable_next_block', + 'enable_next_block', + 'current_line', + 'outputlines', + + '_saved_files_changed', + '_enterspan_replacer', '_leavespan_replacer', - '_saved_files_changed', - # state '_inside_htmlblock', '_inside_codeblock', '_inside_indented_codeblock', @@ -83,47 +112,77 @@ class Po2Md: '_ol_marks', '_current_list_type', '_current_wikilink_target', - '_link_references', + 'link_references', } def __init__(self, pofiles, ignore=[], po_encoding=None, **kwargs): + #: list(str): Paths to PO files to translate. self.pofiles = paths_or_globs_to_unique_pofiles( pofiles, ignore, po_encoding=po_encoding, ) + #: list(str): MD4C extensions used to parse the content. + #: See all available in :doc:`/devref/mdpo/mdpo.md4c`. self.extensions = kwargs.get( 'extensions', DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS, ) + #: dict: Custom events excuted during the parsing while + #: translating content. 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: - self.events[event_name] = [] - self.events[event_name].append(function) - - self._current_msgid = '' - self._current_msgctxt = None - self._current_tcomment = None - self._current_line = '' - self._outputlines = [] - - self._disable_next_block = False - self._disable = False - self._enable_next_block = False + add_debug_events('po2md', self.events) + + #: str: The msgid being currently built for the next msgstr + #: translation. Keep in mind that, if you are executing an event + #: that will be followed by an span one (``enter_span`` or + #: ``exit_span``), the content of the msgid will change before + #: translate it. + self.current_msgid = '' + + #: str: Translator comment that will be translated in the next + #: msgid. + self.current_tcomment = None + + #: str: Context message that will be translated in the next + #: msgid. + self.current_msgctxt = None + + #: str: Line currently being saved in the output. + self.current_line = '' + + #: str: Lines currently added to the output. + self.outputlines = [] + + #: bool: Indicates if the translator is currently disabled, + #: which happens after a ```` command + #: is found, before any subsecuents ```` + #: commands. + self.disable = False + + #: bool: Indicates if the next block will be translated. + self.disable_next_block = False + + #: bool: Indicates if the next block will be translated + #: when the translator is disabled + self.enable_next_block = False + + #: list(:py:class:`polib.POEntry`): Disabled PO entries self.disabled_entries = [] + #: list(:py:class:`polib.POEntry`): Translated PO entries self.translated_entries = [] self.translations = None self.translations_with_msgctxt = None - self.command_aliases = normalize_mdpo_command_aliases( - kwargs.get('command_aliases', {}), + self.command_aliases = ( + normalize_mdpo_command_aliases(kwargs['command_aliases']) + if 'command_aliases' in kwargs else {} ) self.wrapwidth = ( @@ -212,7 +271,7 @@ def __init__(self, pofiles, ignore=[], po_encoding=None, **kwargs): self._current_aspan_href = None self._current_aspan_text = '' - self._link_references = None + self.link_references = None self._current_imgspan = {} # current table head alignments @@ -251,21 +310,21 @@ def command(self, mdpo_command, comment, original_command): 'mdpo-disable-next-block', 'mdpo-disable-next-line', ): - self._disable_next_block = True + self.disable_next_block = True elif mdpo_command == 'mdpo-disable': - self._disable = True + self.disable = True elif mdpo_command == 'mdpo-enable': - self._disable = False + self.disable = False elif mdpo_command in ( 'mdpo-enable-next-block', 'mdpo-enable-next-line', ): - self._enable_next_block = True + self.enable_next_block = True elif comment: if mdpo_command == 'mdpo-context': - self._current_msgctxt = comment + self.current_msgctxt = comment elif mdpo_command == 'mdpo-translator': - self._current_tcomment = comment + self.current_tcomment = comment def _process_command(self, text): original_command, comment = parse_mdpo_html_command(text) @@ -311,29 +370,29 @@ def _save_current_msgid(self): self.events, 'msgid', self, - self._current_msgid, + self.current_msgid, None, - self._current_msgctxt, - self._current_tcomment, + self.current_msgctxt, + self.current_tcomment, [], ): return - if (not self._disable and not self._disable_next_block) or \ - self._enable_next_block: + if (not self.disable and not self.disable_next_block) or \ + self.enable_next_block: translation = self._translate_msgid( - self._current_msgid, - self._current_msgctxt, - self._current_tcomment, + self.current_msgid, + self.current_msgctxt, + self.current_tcomment, ) else: - translation = self._current_msgid + translation = self.current_msgid self.disabled_entries.append( polib.POEntry( msgid=translation, msgstr='', - msgctxt=self._current_msgctxt, - tcomment=self._current_tcomment, + msgctxt=self.current_msgctxt, + tcomment=self.current_tcomment, ), ) @@ -379,14 +438,14 @@ def _save_current_msgid(self): translation = translation.rstrip('\n') if translation.rstrip('\n'): - self._current_line += translation + self.current_line += translation - self._current_msgid = '' - self._current_msgctxt = None - self._current_tcomment = None + self.current_msgid = '' + self.current_msgctxt = None + self.current_tcomment = None - self._disable_next_block = False - self._enable_next_block = False + self.disable_next_block = False + self.enable_next_block = False self._codespan_inside_current_msgid = False self._aimg_title_inside_current_msgid = False @@ -394,8 +453,8 @@ def _save_current_msgid(self): def _save_current_line(self): # strip all spaces according to unicodedata database ignoring newlines, # see https://docs.python.org/3/library/stdtypes.html#str.splitlines - self._outputlines.append(self._current_line.rstrip(' \v\x0b\f\x0c')) - self._current_line = '' + self.outputlines.append(self.current_line.rstrip(' \v\x0b\f\x0c')) + self.current_line = '' def enter_block(self, block, details): # raise 'enter_block' event @@ -409,13 +468,13 @@ def enter_block(self, block, details): if ( self._inside_quoteblock - and (not self._current_line or self._current_line[0] != '>') + and (not self.current_line or self.current_line[0] != '>') and not self._current_thead_aligns and block not in { md4c.BlockType.TABLE, md4c.BlockType.THEAD, md4c.BlockType.TR, } ): - self._current_line += '> ' + self.current_line += '> ' if block is md4c.BlockType.P: self._inside_pblock = True elif block is md4c.BlockType.CODE: @@ -424,24 +483,24 @@ def enter_block(self, block, details): if self._inside_liblock: self._save_current_msgid() - if self._current_line: + if self.current_line: self._save_current_line() indent += ' ' * len(self._current_list_type) if details['fence_char'] is not None: fence_chars = details['fence_char'] * 3 - self._current_line += f'{indent}{fence_chars}' + self.current_line += f'{indent}{fence_chars}' if details['lang']: - self._current_line += details['lang'][0][1] + self.current_line += details['lang'][0][1] else: self._inside_indented_codeblock = True - if self._current_line: + if self.current_line: self._save_current_line() elif block is md4c.BlockType.H: self._inside_hblock = True hash_signs = '#' * details['level'] - self._current_line += f'{hash_signs} ' + self.current_line += f'{hash_signs} ' elif block is md4c.BlockType.LI: if self._current_list_type[-1][0] == 'ol': # inside OL @@ -451,15 +510,15 @@ def enter_block(self, block, details): self._save_current_line() self._ol_marks[-1][0] += 1 indent = ' ' * (len(self._current_list_type) - 1) - self._current_line += f'{indent}1{self._ol_marks[-1][1]} ' + self.current_line += f'{indent}1{self._ol_marks[-1][1]} ' self._current_list_type[-1][-1].append(False) else: # inside UL indent = ' ' * (len(self._current_list_type) - 1) - self._current_line += f'{indent}{self._ul_marks[-1]} ' + self.current_line += f'{indent}{self._ul_marks[-1]} ' if details['is_task']: mark = details['task_mark'] - self._current_line += f'[{mark}] ' + self.current_line += f'[{mark}] ' self._current_list_type[-1][-1].append(details['is_task']) self._inside_liblock = True self._inside_liblock_first_p = True @@ -473,27 +532,27 @@ def enter_block(self, block, details): self._current_list_type.append(['ol', []]) self._ol_marks.append([0, details['mark_delimiter']]) elif block is md4c.BlockType.HR: - if self._current_list_type and not self._current_line: + if self._current_list_type and not self.current_line: self._save_current_line() indent = ( ' ' * len(self._current_list_type) - if not self._current_line.startswith(('- ', '> - ')) else '' + if not self.current_line.startswith(('- ', '> - ')) else '' ) - self._current_line += f'{indent}***' + self.current_line += f'{indent}***' self._save_current_line() if not self._inside_liblock: if self._inside_quoteblock: - self._current_line += f'{indent}>' + self.current_line += f'{indent}>' self._save_current_line() elif block is md4c.BlockType.TR: - self._current_line += ' ' * len(self._current_list_type) + self.current_line += ' ' * len(self._current_list_type) if self._inside_quoteblock and self._current_thead_aligns: - self._current_line += '> ' + self.current_line += '> ' elif block is md4c.BlockType.TH: - self._current_line += '| ' + self.current_line += '| ' self._current_thead_aligns.append(details['align'].value) elif block is md4c.BlockType.TD: - self._current_line += '| ' + self.current_line += '| ' elif block is md4c.BlockType.QUOTE: if self._inside_liblock: self._save_current_msgid() @@ -522,21 +581,21 @@ def leave_block(self, block, details): if self._inside_liblock: if self._inside_quoteblock: indent = ' ' * len(self._current_list_type) - self._current_line = f'{indent}{self._current_line}' + self.current_line = f'{indent}{self.current_line}' self._save_current_line() else: if self._inside_liblock_first_p: self._inside_liblock_first_p = False else: indent = ' ' * len(self._current_list_type) - self._current_line = f'\n{indent}{self._current_line}' + self.current_line = f'\n{indent}{self.current_line}' self._save_current_line() else: self._save_current_line() self._inside_pblock = False if self._inside_quoteblock: - self._current_line = '>' + self.current_line = '>' self._save_current_line() elif block is md4c.BlockType.CODE: @@ -546,11 +605,11 @@ def leave_block(self, block, details): indent = '' if self._inside_liblock: indent += ' ' * len(self._current_list_type) - self._current_line = self._current_line.rstrip('\n') + self.current_line = self.current_line.rstrip('\n') self._save_current_line() if not self._inside_indented_codeblock: fence_chars = details['fence_char']*3 - self._current_line += f'{indent}{fence_chars}' + self.current_line += f'{indent}{fence_chars}' self._save_current_line() if not self._inside_liblock: @@ -562,37 +621,37 @@ def leave_block(self, block, details): self._save_current_msgid() if self._inside_quoteblock: self._save_current_line() - self._current_line += '> ' + self.current_line += '> ' else: - self._current_line += '\n' + self.current_line += '\n' self._save_current_line() if self._inside_quoteblock: - self._current_line += '> ' + self.current_line += '> ' self._inside_hblock = False elif block is md4c.BlockType.LI: self._save_current_msgid() self._inside_liblock = False - if self._current_line: + if self.current_line: self._save_current_line() elif block is md4c.BlockType.UL: self._ul_marks.pop() self._current_list_type.pop() if self._inside_quoteblock: - self._current_line += '> ' - if not self._ul_marks and self._outputlines[-1].split('\n')[-1]: + self.current_line += '> ' + if not self._ul_marks and self.outputlines[-1].split('\n')[-1]: self._save_current_line() elif block is md4c.BlockType.OL: self._ol_marks.pop() self._current_list_type.pop() if self._inside_quoteblock: - self._current_line += '> ' - if not self._ol_marks and self._outputlines[-1]: + self.current_line += '> ' + if not self._ol_marks and self.outputlines[-1]: self._save_current_line() elif block in {md4c.BlockType.TH, md4c.BlockType.TD}: self._save_current_msgid() - self._current_line += ' ' + self.current_line += ' ' elif block is md4c.BlockType.TR: - self._current_line += '|' + self.current_line += '|' self._save_current_line() elif block is md4c.BlockType.THEAD: # build thead separator @@ -610,11 +669,11 @@ def leave_block(self, block, details): thead_separator += '| --: ' indent = ' ' * len(self._current_list_type) - self._current_line += f'{indent}{thead_separator}|' + self.current_line += f'{indent}{thead_separator}|' self._save_current_line() elif block is md4c.BlockType.QUOTE: - if self._outputlines[-1] == '>': - self._outputlines.pop() + if self.outputlines[-1] == '>': + self.outputlines.pop() if not self._inside_liblock: self._save_current_line() self._inside_quoteblock = False @@ -645,22 +704,22 @@ def enter_span(self, span, details): pass else: try: - self._current_msgid += self._enterspan_replacer[span.value] + self.current_msgid += self._enterspan_replacer[span.value] except KeyError: pass if span is md4c.SpanType.A: self._inside_aspan = True - if self._link_references is None: - self._link_references = parse_link_references(self.content) + if self.link_references is None: + self.link_references = parse_link_references(self.content) self._current_aspan_href = details['href'][0][1] self._current_aspan_ref_target = None if details['title']: current_aspan_title = details['title'][0][1] - for target, href, title in self._link_references: + for target, href, title in self.link_references: if ( href == self._current_aspan_href and title == current_aspan_title @@ -668,17 +727,17 @@ def enter_span(self, span, details): self._current_aspan_ref_target = target break else: - for target, href, _ in self._link_references: + for target, href, _ in self.link_references: if href == self._current_aspan_href: self._current_aspan_ref_target = target break elif span is md4c.SpanType.CODE: self._inside_codespan = True - self._codespan_start_index = len(self._current_msgid)-1 + self._codespan_start_index = len(self.current_msgid)-1 self._codespan_inside_current_msgid = True elif span is md4c.SpanType.IMG: - if self._link_references is None: - self._link_references = parse_link_references(self.content) + if self.link_references is None: + self.link_references = parse_link_references(self.content) self._current_imgspan['title'] = '' if not details['title'] \ else details['title'][0][1] @@ -699,7 +758,7 @@ def leave_span(self, span, details): return if span is md4c.SpanType.WIKILINK: - self._current_msgid += polib.escape(self._current_wikilink_target) + self.current_msgid += polib.escape(self._current_wikilink_target) self._current_wikilink_target = None if self._inside_aspan: # span inside link text @@ -711,42 +770,42 @@ def leave_span(self, span, details): pass else: try: - self._current_msgid += self._leavespan_replacer[span.value] + self.current_msgid += self._leavespan_replacer[span.value] except KeyError: pass if span is md4c.SpanType.A: if self._current_aspan_ref_target: # referenced link - self._current_msgid += f'[{self._current_aspan_text}]' + self.current_msgid += f'[{self._current_aspan_text}]' if self._current_aspan_ref_target != self._current_aspan_text: - self._current_msgid += ( + self.current_msgid += ( f'[{self._current_aspan_ref_target}]' ) self._current_aspan_ref_target = None else: if self._current_aspan_text == self._current_aspan_href: # autolink vs link clash (see implementation notes) - self._current_msgid += f'<{self._current_aspan_text}' + self.current_msgid += f'<{self._current_aspan_text}' if details['title']: escaped_title = polib.escape(details['title'][0][1]) - self._current_msgid += f' "{escaped_title}"' - self._current_msgid += '>' + self.current_msgid += f' "{escaped_title}"' + self.current_msgid += '>' elif self._current_aspan_href: - self._current_msgid += ( + self.current_msgid += ( f'[{self._current_aspan_text}]' f'({self._current_aspan_href}' ) if details['title']: self._aimg_title_inside_current_msgid = True escaped_title = polib.escape(details['title'][0][1]) - self._current_msgid += f' "{escaped_title}"' - self._current_msgid += ')' + self.current_msgid += f' "{escaped_title}"' + self.current_msgid += ')' self._current_aspan_href = None self._inside_aspan = False self._current_aspan_text = '' elif span is md4c.SpanType.CODE: self._inside_codespan = False - self._current_msgid += ( + self.current_msgid += ( self._codespan_backticks * self.code_end_string ) self._codespan_backticks = None @@ -755,12 +814,12 @@ def leave_span(self, span, details): imgspan_src = details['src'][0][1] if details['title']: imgspan_title = polib.escape(details['title'][0][1]) - for target, href, title in self._link_references: + for target, href, title in self.link_references: if href == imgspan_src and title == imgspan_title: referenced_target = target break else: - for target, href, _ in self._link_references: + for target, href, _ in self.link_references: if href == imgspan_src: referenced_target = target break @@ -778,7 +837,7 @@ def leave_span(self, span, details): if self._inside_aspan: self._current_aspan_text += img_markup else: - self._current_msgid += img_markup + self.current_msgid += img_markup self._current_imgspan = {} @@ -805,10 +864,10 @@ def text(self, block, text): self.code_start_string, text, ) - 1 - self._current_msgid = ( - f'{self._current_msgid[:self._codespan_start_index]}' + self.current_msgid = ( + f'{self.current_msgid[:self._codespan_start_index]}' f'{self._codespan_backticks * self.code_start_string}' - f'{self._current_msgid[self._codespan_start_index:]}' + f'{self.current_msgid[self._codespan_start_index:]}' ) if self._inside_aspan: self._current_aspan_text += text @@ -837,26 +896,26 @@ def text(self, block, text): f'{self._current_wikilink_target}|{text}' ) return - self._current_msgid += text + self.current_msgid += text else: if self._inside_liblock: indent = ' ' * len(self._current_list_type) - if self._current_line[:len(indent)+1] != indent: - self._current_line += indent - self._current_msgid += text + if self.current_line[:len(indent)+1] != indent: + self.current_line += indent + self.current_msgid += text else: self._process_command(text) def _append_link_references(self): - if self._link_references: - self._disable_next_block = False - self._disable = False + if self.link_references: + self.disable_next_block = False + self.disable = False # 'link_reference' event pre_events = self.events.get('link_reference') added_references = [] # don't repeat references - for target, href, title in self._link_references: + for target, href, title in self.link_references: if pre_events: skip = False for event in pre_events: @@ -870,11 +929,11 @@ def _append_link_references(self): continue msgid = f'[{target}]:{href_title}' - self._outputlines.append( + self.outputlines.append( self._translate_msgid(msgid, None, None), ) added_references.append(href_title) - self._outputlines.append('') + self.outputlines.append('') def translate( self, @@ -905,12 +964,12 @@ def translate( ) self._append_link_references() # add link references to the end - self._disable_next_block = False - self._disable = False - self._enable_next_block = False - self._link_references = None + self.disable_next_block = False + self.disable = False + self.enable_next_block = False + self.link_references = None - self.output = '\n'.join(self._outputlines) + self.output = '\n'.join(self.outputlines) if save: if self._saved_files_changed is False: diff --git a/setup.cfg b/setup.cfg index 9cb779e8..4d8ba65b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = mdpo -version = 0.3.83 +version = 0.3.84 description = Markdown files translation using PO files. long_description = file: README.md long_description_content_type = text/markdown @@ -135,7 +135,7 @@ sections = STDLIB,THIRDPARTY,FIRSTPARTY,TESTS,LOCALFOLDER [build_sphinx] project = mdpo -version = 0.3.83 -release = 0.3.83 +version = 0.3.84 +release = 0.3.84 source-dir = docs build-dir = docs/_build diff --git a/test/conftest.py b/test/conftest.py index 68cab0cd..81d3de57 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -57,14 +57,16 @@ def __init__(self, *args, **kwargs): self.slots = [] def visit_ClassDef(self, node): - if ( - isinstance(node.body[0], ast.Assign) - and node.body[0].targets[0].id == '__slots__' - ): - self.slots.extend([ - getattr(elt, AST_ELTS_VALUE_ATTR) - for elt in node.body[0].value.elts - ]) + for child in node.body: + if ( + isinstance(child, ast.Assign) + and child.targets[0].id == '__slots__' + ): + self.slots.extend([ + getattr(elt, AST_ELTS_VALUE_ATTR) + for elt in child.value.elts + ]) + break modtree = ast.parse(inspect.getsource(code)) visitor = ClassSlotsExtractor() diff --git a/test/test_unit/test_md2po/test_md2po_cli.py b/test/test_unit/test_md2po/test_md2po_cli.py index b087b19c..b8341e6e 100644 --- a/test/test_unit/test_md2po/test_md2po_cli.py +++ b/test/test_unit/test_md2po/test_md2po_cli.py @@ -687,7 +687,7 @@ def test_events(arg, tmp_file, capsys): event_file = ''' def transform_text(self, block, text): if text == "Foo": - self._current_msgid = "Bar" + self.current_msgid = "Bar" return False ''' diff --git a/test/test_unit/test_po2md/test_po2md_cli.py b/test/test_unit/test_po2md/test_po2md_cli.py index d147b390..220483a1 100644 --- a/test/test_unit/test_po2md/test_po2md_cli.py +++ b/test/test_unit/test_po2md/test_po2md_cli.py @@ -166,7 +166,7 @@ def test_events(arg, tmp_file, capsys): event_file = ''' def transform_text(self, block, text): if text == "Foo": - self._current_msgid = "Bar" + self.current_msgid = "Bar" return False ''' diff --git a/test/test_unit/test_po2md/test_po2md_events.py b/test/test_unit/test_po2md/test_po2md_events.py index e601fe03..28499a2f 100644 --- a/test/test_unit/test_po2md/test_po2md_events.py +++ b/test/test_unit/test_po2md/test_po2md_events.py @@ -70,7 +70,7 @@ def process_footnote_references(self, target, href, title): def test_command_event(tmp_file): def _abort_command(self, *args): - assert not self._disable + assert not self.disable return False input_content = '''