1818
1919
2020with imports_under_tool ("i18n" ):
21- from pygettext import parse_spec
21+ from pygettext import (parse_spec , process_keywords , DEFAULTKEYWORDS ,
22+ unparse_spec )
2223
2324
2425def normalize_POT_file (pot ):
@@ -483,20 +484,22 @@ def test_comments_not_extracted_without_tags(self):
483484
484485 def test_parse_keyword_spec (self ):
485486 valid = (
486- ('foo' , ('foo' , {0 : 'msgid' })),
487- ('foo:1' , ('foo' , {0 : 'msgid' })),
488- ('foo:1,2' , ('foo' , {0 : 'msgid' , 1 : 'msgid_plural' })),
489- ('foo:1, 2' , ('foo' , {0 : 'msgid' , 1 : 'msgid_plural' })),
490- ('foo:1,2c' , ('foo' , {0 : 'msgid' , 1 : 'msgctxt' })),
491- ('foo:2c,1' , ('foo' , {0 : 'msgid' , 1 : 'msgctxt' })),
492- ('foo:2c ,1' , ('foo' , {0 : 'msgid' , 1 : 'msgctxt' })),
493- ('foo:1,2,3c' , ('foo' , {0 : 'msgid' , 1 : 'msgid_plural' , 2 : 'msgctxt' })),
494- ('foo:1, 2, 3c' , ('foo' , {0 : 'msgid' , 1 : 'msgid_plural' , 2 : 'msgctxt' })),
495- ('foo:3c,1,2' , ('foo' , {0 : 'msgid' , 1 : 'msgid_plural' , 2 : 'msgctxt' })),
487+ ('foo' , ('foo' , {'msgid' : 0 })),
488+ ('foo:1' , ('foo' , {'msgid' : 0 })),
489+ ('foo:1,2' , ('foo' , {'msgid' : 0 , 'msgid_plural' : 1 })),
490+ ('foo:1, 2' , ('foo' , {'msgid' : 0 , 'msgid_plural' : 1 })),
491+ ('foo:1,2c' , ('foo' , {'msgid' : 0 , 'msgctxt' : 1 })),
492+ ('foo:2c,1' , ('foo' , {'msgid' : 0 , 'msgctxt' : 1 })),
493+ ('foo:2c ,1' , ('foo' , {'msgid' : 0 , 'msgctxt' : 1 })),
494+ ('foo:1,2,3c' , ('foo' , {'msgid' : 0 , 'msgid_plural' : 1 , 'msgctxt' : 2 })),
495+ ('foo:1, 2, 3c' , ('foo' , {'msgid' : 0 , 'msgid_plural' : 1 , 'msgctxt' : 2 })),
496+ ('foo:3c,1,2' , ('foo' , {'msgid' : 0 , 'msgid_plural' : 1 , 'msgctxt' : 2 })),
496497 )
497498 for spec , expected in valid :
498499 with self .subTest (spec = spec ):
499500 self .assertEqual (parse_spec (spec ), expected )
501+ # test unparse-parse round-trip
502+ self .assertEqual (parse_spec (unparse_spec (* expected )), expected )
500503
501504 invalid = (
502505 ('foo:' , "Invalid keyword spec 'foo:': missing argument positions" ),
@@ -516,6 +519,70 @@ def test_parse_keyword_spec(self):
516519 parse_spec (spec )
517520 self .assertEqual (str (cm .exception ), message )
518521
522+ def test_process_keywords (self ):
523+ default_keywords = {name : [spec ] for name , spec
524+ in DEFAULTKEYWORDS .items ()}
525+ inputs = (
526+ (['foo' ], True ),
527+ (['_:1,2' ], True ),
528+ (['foo' , 'foo:1,2' ], True ),
529+ (['foo' ], False ),
530+ (['_:1,2' , '_:1c,2,3' , 'pgettext' ], False ),
531+ # Duplicate entries
532+ (['foo' , 'foo' ], True ),
533+ (['_' ], False )
534+ )
535+ expected = (
536+ {'foo' : [{'msgid' : 0 }]},
537+ {'_' : [{'msgid' : 0 , 'msgid_plural' : 1 }]},
538+ {'foo' : [{'msgid' : 0 }, {'msgid' : 0 , 'msgid_plural' : 1 }]},
539+ default_keywords | {'foo' : [{'msgid' : 0 }]},
540+ default_keywords | {'_' : [{'msgid' : 0 , 'msgid_plural' : 1 },
541+ {'msgctxt' : 0 , 'msgid' : 1 , 'msgid_plural' : 2 },
542+ {'msgid' : 0 }],
543+ 'pgettext' : [{'msgid' : 0 },
544+ {'msgctxt' : 0 , 'msgid' : 1 }]},
545+ {'foo' : [{'msgid' : 0 }]},
546+ default_keywords ,
547+ )
548+ for (keywords , no_default_keywords ), expected in zip (inputs , expected ):
549+ with self .subTest (keywords = keywords ,
550+ no_default_keywords = no_default_keywords ):
551+ processed = process_keywords (
552+ keywords ,
553+ no_default_keywords = no_default_keywords )
554+ self .assertEqual (processed , expected )
555+
556+ def test_multiple_keywords_same_funcname_errors (self ):
557+ # If at least one keyword spec for a given funcname matches,
558+ # no error should be printed.
559+ msgids , stderr = self .extract_from_str (dedent ('''\
560+ _("foo", 42)
561+ _(42, "bar")
562+ ''' ), args = ('--keyword=_:1' , '--keyword=_:2' ), with_stderr = True )
563+ self .assertIn ('foo' , msgids )
564+ self .assertIn ('bar' , msgids )
565+ self .assertEqual (stderr , b'' )
566+
567+ # If no keyword spec for a given funcname matches,
568+ # all errors are printed.
569+ msgids , stderr = self .extract_from_str (dedent ('''\
570+ _(x, 42)
571+ _(42, y)
572+ ''' ), args = ('--keyword=_:1' , '--keyword=_:2' ), with_stderr = True ,
573+ strict = False )
574+ self .assertEqual (msgids , ['' ])
575+ # Normalize line endings on Windows
576+ stderr = stderr .decode ('utf-8' ).replace ('\r ' , '' )
577+ self .assertEqual (
578+ stderr ,
579+ '*** test.py:1: No keywords matched gettext call "_":\n '
580+ '\t keyword="_": Expected a string constant for argument 1, got x\n '
581+ '\t keyword="_:2": Expected a string constant for argument 2, got 42\n '
582+ '*** test.py:2: No keywords matched gettext call "_":\n '
583+ '\t keyword="_": Expected a string constant for argument 1, got 42\n '
584+ '\t keyword="_:2": Expected a string constant for argument 2, got y\n ' )
585+
519586
520587def extract_from_snapshots ():
521588 snapshots = {
@@ -526,6 +593,10 @@ def extract_from_snapshots():
526593 'custom_keywords.py' : ('--keyword=foo' , '--keyword=nfoo:1,2' ,
527594 '--keyword=pfoo:1c,2' ,
528595 '--keyword=npfoo:1c,2,3' , '--keyword=_:1,2' ),
596+ 'multiple_keywords.py' : ('--keyword=foo:1c,2,3' , '--keyword=foo:1c,2' ,
597+ '--keyword=foo:1,2' ,
598+ # repeat a keyword to make sure it is extracted only once
599+ '--keyword=foo' , '--keyword=foo' ),
529600 # == Test character escaping
530601 # Escape ascii and unicode:
531602 'escapes.py' : ('--escape' , '--add-comments=' ),
0 commit comments