Skip to content

Commit

Permalink
feat: add new options merge-js and no-partial
Browse files Browse the repository at this point in the history
no-partial: no partial files are generated, because they replaced the original django.po and djangojs.po
merge-js: no djangojs*.po is generated, because it has been merged into django*.po

Refs: FC-0012 OEP-58
  • Loading branch information
shadinaif committed Sep 4, 2023
1 parent 27025f4 commit cc31a71
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 16 deletions.
49 changes: 45 additions & 4 deletions i18n/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from path import Path

from i18n import Runner
from i18n.execute import execute
from i18n.execute import remove_file, execute
from i18n.segment import segment_pofiles


Expand Down Expand Up @@ -65,6 +65,22 @@ def add_args(self):
Adds arguments
"""
self.parser.description = __doc__
self.parser.add_argument(
'--merge-js',
action='store_true',
help='Merge djangojs.po with django.po using msgcat'
)
self.parser.add_argument(
'--no-partial',
action='store_true',
help=(
'Renames (django-partial.po) and (djangojs-partial.po) to (django.po) and (djangojs.po) respectively. '
'This will also remove (django-saved.po) and (djangojs-saved.po) that are used to restore original '
'(django.po) and (djangojs.po) after extract is completed. '
'This is helpful when this `extract` command output is needed to be used immediately '
'with no segment/merge workflow in small repositories.'
)
)

def rename_source_file(self, src, dst):
"""
Expand Down Expand Up @@ -157,9 +173,19 @@ def run(self, args):
for filename in files_to_clean:
clean_pofile(self.source_msgs_dir.joinpath(filename))

# Restore the saved .po files.
self.rename_source_file(DJANGO_SAVED_PO, DJANGO_PO)
self.rename_source_file(DJANGOJS_SAVED_PO, DJANGOJS_PO)
if args.merge_js:
self.merge_js(stderr)

if args.no_partial:
# Overwrite django.po and djangojs.po from django-partial.po and djangojs-partial.po
self.rename_source_file(DJANGO_PARTIAL_PO, DJANGO_PO)
self.rename_source_file(DJANGOJS_PARTIAL_PO, DJANGOJS_PO)
remove_file(self.source_msgs_dir.joinpath(DJANGO_SAVED_PO))
remove_file(self.source_msgs_dir.joinpath(DJANGOJS_SAVED_PO))
else:
# Restore the saved .po files.
self.rename_source_file(DJANGO_SAVED_PO, DJANGO_PO)
self.rename_source_file(DJANGOJS_SAVED_PO, DJANGOJS_PO)

def babel_extract(self, stderr, verbosity):
"""
Expand Down Expand Up @@ -197,6 +223,21 @@ def babel_extract(self, stderr, verbosity):

execute(babel_underscore_cmd, working_directory=configuration.root_dir, stderr=stderr)

def merge_js(self, stderr):
"""
Merge djangojs.po with django.po using msgcat
"""
# Some projects don't have any javascript, so there is no djangojs-partial.po
if not file_exists(self.source_msgs_dir.joinpath()):
return

execute(
'msgcat django-partial.po djangojs-partial.po -o django-partial.po',
working_directory=self.source_msgs_dir,
stderr=stderr,
)
remove_file(self.source_msgs_dir.joinpath(DJANGOJS_PARTIAL_PO))


def clean_pofile(path_name):
"""
Expand Down
85 changes: 73 additions & 12 deletions tests/test_extract.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,52 @@
import os
from datetime import datetime, timedelta
from functools import wraps
import itertools

import ddt
import polib
from i18n import extract, config
from path import Path

from . import I18nToolTestCase, MOCK_DJANGO_APP_DIR


def perform_extract():
def perform_extract_with_options():
"""
Decorator for test methods in TestExtract class.
It wraps the test method in a function that calls (extract.main) with various options using (ddt.data)
Sets the following attributes:
- ddt_flag_merge_js: True if the test method should be run with merge-js=True
- ddt_flag_no_partial: True if the test method should be run with no-partial=True
"""
def decorator(test_method):
"""
The decorator itself
"""
@wraps(test_method)
def wrapped(self):
@ddt.data(*itertools.product([True, False], repeat=2)) # all combinations of flags
@ddt.unpack
def wrapped(self, flag_merge_js, flag_no_partial):
"""
The wrapper function
"""
self.ddt_flag_merge_js = flag_merge_js
self.ddt_flag_no_partial = flag_no_partial
extract.main(
verbosity=0,
config=self.configuration._filename,
root_dir=MOCK_DJANGO_APP_DIR,
merge_js=flag_merge_js,
no_partial=flag_no_partial,
)
test_method(self)
return wrapped
return decorator


@ddt.ddt
class TestExtract(I18nToolTestCase):
"""
Tests functionality of i18n/extract.py
Expand All @@ -52,27 +67,40 @@ def setUp(self):
)
self.configuration = config.Configuration(root_dir=MOCK_DJANGO_APP_DIR)

# These will be set by extract_options decorator. Also get_files will fail if they are None (to remind us
# to use the decorator in all tests methods)
self.ddt_flag_merge_js = None
self.ddt_flag_no_partial = None

@property
def django_po(self):
"""
Returns the name of the generated django file
Returns file or partial file name according to the no-partial flag
"""
return extract.DJANGO_PARTIAL_PO
return extract.DJANGO_PO if self.ddt_flag_no_partial else extract.DJANGO_PARTIAL_PO

@property
def djangojs_po(self):
"""
Returns the name of the generated djangojs file
Returns jsfile or partial jsfile name according to the no-partial flag
"""
return extract.DJANGOJS_PARTIAL_PO
return extract.DJANGOJS_PO if self.ddt_flag_no_partial else extract.DJANGOJS_PARTIAL_PO

def get_files(self):
"""
This is a generator.
Returns the fully expanded filenames for all extracted files
Fails assertion if one of the files doesn't exist.
"""
generated_files = (extract.MAKO_PO, self.django_po, self.djangojs_po,)
assert self.ddt_flag_merge_js is not None, "Use perform_extract decorator"
assert self.ddt_flag_no_partial is not None, "Use perform_extract decorator"

# Depending on how the merge-js and no-partial options are set, we may have generated different files
# no-partial: no partial files are generated, because they replaced the original django.po and djangojs.po
# merge-js: no djangojs*.po is generated, because it has been merged into django*.po
generated_files = ('mako.po', self.django_po,)
if not self.ddt_flag_merge_js:
generated_files += (self.djangojs_po,)

for filename in generated_files:
path = Path.joinpath(self.configuration.source_messages_dir, filename)
Expand All @@ -81,7 +109,7 @@ def get_files(self):

yield path

@perform_extract()
@perform_extract_with_options()
def test_files(self):
"""
Asserts that each auto-generated file has been modified since 'extract' was launched.
Expand All @@ -91,7 +119,7 @@ def test_files(self):
self.assertTrue(datetime.fromtimestamp(os.path.getmtime(path)) > self.start_time,
msg='File not recently modified: %s' % path)

@perform_extract()
@perform_extract_with_options()
def test_is_keystring(self):
"""
Verifies is_keystring predicate
Expand All @@ -103,7 +131,7 @@ def test_is_keystring(self):
self.assertTrue(extract.is_key_string(entry1.msgid))
self.assertFalse(extract.is_key_string(entry2.msgid))

@perform_extract()
@perform_extract_with_options()
def test_headers(self):
"""
Verify all headers have been modified
Expand All @@ -116,7 +144,7 @@ def test_headers(self):
msg='Missing header in %s:\n"%s"' % (path, header)
)

@perform_extract()
@perform_extract_with_options()
def test_metadata(self):
"""
Verify all metadata has been modified
Expand All @@ -128,7 +156,7 @@ def test_metadata(self):
expected = '[email protected]'
self.assertEquals(expected, value)

@perform_extract()
@perform_extract_with_options()
def test_metadata_no_create_date(self):
"""
Verify `POT-Creation-Date` metadata has been removed
Expand All @@ -137,3 +165,36 @@ def test_metadata_no_create_date(self):
po = polib.pofile(path)
metadata = po.metadata
self.assertIsNone(metadata.get('POT-Creation-Date'))

@perform_extract_with_options()
def test_merge_js(self):
"""
Verify that djangojs*.po is generated only if merge-js is False
"""
assert self.ddt_flag_merge_js != Path.exists(
Path.joinpath(self.configuration.source_messages_dir, self.djangojs_po,)
)

@perform_extract_with_options()
def test_no_partial_guard_to_verify_names_in_tests(self):
"""
Verify that (django_po) and (djangojs_po) properties return the correct file names
according to no-partial flag
"""
assert self.ddt_flag_no_partial != ('-partial' in self.django_po)
assert self.ddt_flag_no_partial != ('-partial' in self.djangojs_po)

@perform_extract_with_options()
def test_no_partial(self):
"""
Verify that partial files are not generated if no-partial is True
"""
# We can't use (django_po) and (djangojs_po) properties here because we need to always check
# for (partial) files
assert self.ddt_flag_no_partial != Path.exists(
Path.joinpath(self.configuration.source_messages_dir, extract.DJANGO_PARTIAL_PO,)
)
if not self.ddt_flag_merge_js:
assert self.ddt_flag_no_partial != Path.exists(
Path.joinpath(self.configuration.source_messages_dir, extract.DJANGOJS_PARTIAL_PO,)
)

0 comments on commit cc31a71

Please sign in to comment.