Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i18n: Introduce i18n.files() metedata wrapper #1757

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

sardemff7
Copy link
Contributor

This would “fix” #1733, #1739 and replace #1630.

It is still RFC as the code is not completely clean and maybe not up to Meson standards (at least the test part).

Would that be an acceptable design?


from os import path
from .. import coredata, mesonlib, build
from ..mesonlib import MesonException
from . import ModuleReturnValue
from . import ExtensionModule
from ..interpreterbase import InterpreterObject, MutableInterpreterObject
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [F401] '..interpreterbase.MutableInterpreterObject' imported but unused

'--add-comments',
'none': {
'args': [],
'keywords' : [],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E203] whitespace before ':'

'--flag=g_error_new:3:c-format',
'--flag=g_set_error:4:c-format',
]
'keywords' : [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E203] whitespace before ':'


def _get_preset(self, kwargs):
preset = kwargs.pop('preset', self.preset)
if not preset in PRESET_ARGS:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E713] test for membership should be 'not in'

self.datadirs = I18nModule._get_data_dirs(self.environment, self.subdir, mesonlib.stringlistify(kwargs.get('data_dirs', [])))

self.preset = kwargs.pop('preset', 'none')
if not self.preset in PRESET_ARGS:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E713] test for membership should be 'not in'

preset = self._get_preset(kwargs)

files = {
'files': [ path.join(self.interpreter.subdir, f) for f in args ],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E201] whitespace after '['
  • [E202] whitespace before ']'

datadirs = '--datadirs=' + ':'.join(self.datadirs) if self.datadirs else None
pot_data_file = None
extra_args = None
preset = self._get_preset(kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [F841] local variable 'preset' is assigned to but never used

extra_args = set(preset_args + extra_args)
preset = PRESET_ARGS.get(preset)
extra_args = set(preset['args'] +
[ '--keyword=' + k for k in preset['keywords'] ] +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E201] whitespace after '['
  • [E202] whitespace before ']'

preset = PRESET_ARGS.get(preset)
extra_args = set(preset['args'] +
[ '--keyword=' + k for k in preset['keywords'] ] +
[ '--flag=' + f for f in preset['flags'] ] +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E201] whitespace after '['
  • [E202] whitespace before ']'

if datafilename:
with open(datafilename, 'rb') as ifile:
files = pickle.load(ifile)
tmp_pot = os.path.join(os.path.dirname(datafilename), 'i18n-' + pkgname + '.pot')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E221] multiple spaces before operator

os.remove(tmp_pot)
join_arg = []
for f in files:
language = [ '--language=' + f['language'] ] if 'language' in f else []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E201] whitespace after '['
  • [E202] whitespace before ']'

join_arg = []
for f in files:
language = [ '--language=' + f['language'] ] if 'language' in f else []
keywords = [ '--keyword=' + k for k in f['keywords'] ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E201] whitespace after '['
  • [E202] whitespace before ']'

for f in files:
language = [ '--language=' + f['language'] ] if 'language' in f else []
keywords = [ '--keyword=' + k for k in f['keywords'] ]
flags = [ '--flag=' + f for f in f['flags'] ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E201] whitespace after '['
  • [E202] whitespace before ']'

keywords = [ '--keyword=' + k for k in f['keywords'] ]
flags = [ '--flag=' + f for f in f['flags'] ]
subprocess.check_call(['xgettext', '--package-name=' + pkgname, '-p', src_sub,
'-D', os.environ['MESON_SOURCE_ROOT'], '-o', tmp_pot] + join_arg
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E128] continuation line under-indented for visual indent

flags = [ '--flag=' + f for f in f['flags'] ]
subprocess.check_call(['xgettext', '--package-name=' + pkgname, '-p', src_sub,
'-D', os.environ['MESON_SOURCE_ROOT'], '-o', tmp_pot] + join_arg
+ language + keywords + flags + f['extra_args'] + f['files'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [W503] line break before binary operator
  • [E128] continuation line under-indented for visual indent

subprocess.check_call(['xgettext', '--package-name=' + pkgname, '-p', src_sub,
'-D', os.environ['MESON_SOURCE_ROOT'], '-o', tmp_pot] + join_arg
+ language + keywords + flags + f['extra_args'] + f['files'],
env=child_env)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E128] continuation line under-indented for visual indent

+ language + keywords + flags + f['extra_args'] + f['files'],
env=child_env)
if os.path.exists(tmp_pot):
join_arg = [ '-j' ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E201] whitespace after '['
  • [E202] whitespace before ']'

@jpakkane
Copy link
Member

It's unfortunate that the user has to write pot.process() to get things running and if they forget it then nothing happens. It the very least it should guard against being called multiple times.

datadirs = '--datadirs=' + ':'.join(self.datadirs) if self.datadirs else None
pot_data_file = None
extra_args = None
preset = self._get_preset(kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [F841] local variable 'preset' is assigned to but never used

flags = ['--flag=' + f for f in f['flags']]
subprocess.check_call(['xgettext', '--package-name=' + pkgname, '-p', src_sub,
'-D', os.environ['MESON_SOURCE_ROOT'], '-o', tmp_pot] + join_arg +
language + keywords + flags + f['extra_args'] + f['files'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [E127] continuation line over-indented for visual indent

@sardemff7
Copy link
Contributor Author

pot.process() is not needed any more, at the cost of a rewrite of the .dat file for each pot.add_files().

@codecov-io
Copy link

codecov-io commented May 23, 2017

Codecov Report

Merging #1757 into master will decrease coverage by 0.01%.
The diff coverage is 59.52%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1757      +/-   ##
==========================================
- Coverage   63.84%   63.82%   -0.02%     
==========================================
  Files          71       71              
  Lines       17086    17238     +152     
  Branches     3517     3556      +39     
==========================================
+ Hits        10908    11002      +94     
- Misses       5098     5137      +39     
- Partials     1080     1099      +19
Impacted Files Coverage Δ
mesonbuild/scripts/gettext.py 0% <0%> (ø) ⬆️
mesonbuild/scripts/msgfmthelper.py 0% <0%> (ø) ⬆️
mesonbuild/modules/i18n.py 68.75% <70.42%> (+3.41%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b52955f...cd1802f. Read the comment docs.

pot.add_merge_files(*file1*, *file2*, ...)
```

This method is a shortcut to `pot.add_files()` then `pot.merge_files()` on a set of files.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a case when you want merge_files() but never to add them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not me, but there is i18n.merge_file() and you’re not forced to call i18n.gettext() so I just wanted to keep that option.
Also, you cannot use extra_args iwth pot.add_merge_files() because there is no way to know which are for the xgettext call and which are for the msgfmt call. Since I wanted to keep extra_args as the standard name, and not having xgettext_extra_args and msgfmt_extra_args, this is the simplest way.

Just let me know what you prefer I’ll happily rework the code to fit. :-)

@sardemff7
Copy link
Contributor Author

Btw, should I write (a) new test(s) or add to the existing one?

@jpakkane
Copy link
Member

Whatever feels most natural to you.

@sardemff7 sardemff7 changed the title [RFC] i18n: Add a pot file object i18n: Add a pot file object Jun 4, 2017
@jpakkane
Copy link
Member

⚔️ conflicts. Also needs a mention in release notes.

@jpakkane
Copy link
Member

This goes against the grain of current design of not using mutable objects. Mutating methods are not nice. Could this be converted behave like other parts do is to have Files() calls in various subdirs and then use all of them in the pot call invocation.

@sardemff7
Copy link
Contributor Author

That would require more than just files(), since we need to associate keywords, language (file language) and xgettext extra args with each set of files. An i18n.files() could work, as a files() wrapper with extra kwargs, then you pass them as a list to i18n.create_pot().
That would be a tiny bit less flexible as for merge_files() since it would not allow future extra_args to the msgfmt call (the reason I added separate pot.add_merge_files() and pot.merge_files() initially) but if you’re fine with a maybe future msgfmt_extra_args kwarg, that’ll do.

Signed-off-by: Quentin Glidic <sardemff7+git@sardemff7.net>
@sardemff7 sardemff7 changed the title i18n: Add a pot file object i18n: Introduce i18n.files() metedata wrapper Jul 15, 2017
elif isinstance(f, mesonlib.File):
files.append(f)
elif isinstance(f, list):
files += self._flatten_files(f, source_dir, subdir)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [F821] undefined name 'self'


MERGE_LANGUAGES = ('xml', 'desktop')
if self.language not in MERGE_LANGUAGES:
raise MesonException('i18n: "{}" is not a valid merge language {}'.format(language, MERGE_LANGUAGES))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [F821] undefined name 'language'

elif isinstance(f, mesonlib.File):
files.append(f)
elif isinstance(f, list):
r = self._flatten_files(f)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[flake8]

  • [F821] undefined name 'self'

Signed-off-by: Quentin Glidic <sardemff7+git@sardemff7.net>
@sardemff7
Copy link
Contributor Author

New design, you now create files object, containing a list of files and metadata, then pass all of them to .create_pot() to actually create all the needed targets.

As said on IRC, there is still an issue, I generate the .pot file in meson-private first (incrementally, with several xgettext calls) but Python fails to move it back to the source dir if they are on different filesystems. They are some possible fixes and workarounds, but if it is an acceptable issue, it’s probably the cleanest design and code.

@sardemff7
Copy link
Contributor Author

As a side note: i18n.files() does not install/merge files alone, it needs to be passed to i18n.create_pot(). I did not put that in the doc, as the files() does not do anything to the files either, and i18n.files() is advertise as a wrapper to add metadata.
Is that clear enough or should I be explicit about that? I was thinking about it in #837 since it could be done as a wrapper to i18n.files() with predefined values and a preset, but then the wrapper itself would also be “inactive” alone.

@jpakkane
Copy link
Member

This is definitely better. But how about doing the encapsulation slightly differently:

pot_files = i18n.pot_file_list()
pot_files.add('foo.c', 'bar.c', ...)
... etc in other subdirs
# then finally do
foo = i18n.create_pot('projname', pot_files)

This would mirror the way configure_data works: first create an object to hold all data, then append stuff to it and finally use it.

@sardemff7
Copy link
Contributor Author

I thought objects where immutable? And that you didn’t like the “it needs final .process() to actually do something”. :-)
I don’t think mirroring configure_data() is meaningful here, we are not creating something, just associating metadata.
There are 3 needs that lead to this design:

  • Meson needs the list of files and associated keywords to create the pot file → you create objects to keep that, then pass them to .create_pot()
  • Meson needs the pot file directory to merge translations → the custom targets are generated in .create_pot()
  • you wanted immutable objects

FTR, the first design was: .create_pot()add_files().process() and the second one was .create_pot()add_files()

@nirbheek
Copy link
Member

FWIW, I like the 'associate metadata with files' design. We might need this for more things in the future, so it is a good precedent. For instance, currently we guess the file type based on the extension throughout meson, but some day we may need to change that.

@krader1961
Copy link
Contributor

Christ on a Cracker. Please close ancient pull-requests like this one. Either it is still relevant and should be merged (possibly with changes) or it is irrelevant and should be rejected.

@sardemff7
Copy link
Contributor Author

IMHO, it is still relevant, even though I didn’t have time to keep it up to date
I just felt like there was no consensus about the macro design of this feature, and that I should wait before re-writing the whole thing again

@jpakkane
Copy link
Member

Why does one need per-file gettext flags? Meson's design has been to not permit those in general, because they add a lot of complexity in both implementing the feature and to build files (i.e. the "where do these unexpected flags in my invocations come from, I never specify them" problem).

@sardemff7
Copy link
Contributor Author

sardemff7 commented Feb 17, 2022

It is less per-file as it is per-language
You may not want to use the same extraction keywords for C or Python files
However, you probably want only one .pot file, so you have to merge all the extractions as some point
You can see the description of #1739 for an example
Please note that some formats (like desktop files) are quite generic and you cannot guess all the extensions people would use (e.g. I use .action in some project)

@TingPing TingPing removed their assignment Feb 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants