1919from babel .core import Locale
2020from babel .messages import pofile
2121from babel .messages .catalog import Catalog , Message
22+ from babel .messages .pofile import _enclose_filename_if_necessary , _extract_locations
2223from babel .util import FixedOffsetTimezone
2324
2425
@@ -438,6 +439,19 @@ def test_missing_plural_in_the_middle(self):
438439 assert message .string [1 ] == ''
439440 assert message .string [2 ] == 'Vohs [text]'
440441
442+ def test_with_location (self ):
443+ buf = StringIO ('''\
444+ #: main.py:1 \u2068 filename with whitespace.py\u2069 :123
445+ msgid "foo"
446+ msgstr "bar"
447+ ''' )
448+ catalog = pofile .read_po (buf , locale = 'de_DE' )
449+ assert len (catalog ) == 1
450+ message = catalog ['foo' ]
451+ assert message .string == 'bar'
452+ assert message .locations == [("main.py" , 1 ), ("filename with whitespace.py" , 123 )]
453+
454+
441455 def test_abort_invalid_po_file (self ):
442456 invalid_po = '''
443457 msgctxt ""
@@ -841,6 +855,59 @@ def test_no_include_lineno(self):
841855msgid "foo"
842856msgstr ""'''
843857
858+ def test_white_space_in_location (self ):
859+ catalog = Catalog ()
860+ catalog .add ('foo' , locations = [('main.py' , 1 )])
861+ catalog .add ('foo' , locations = [('utils b.py' , 3 )])
862+ buf = BytesIO ()
863+ pofile .write_po (buf , catalog , omit_header = True , include_lineno = True )
864+ assert buf .getvalue ().strip () == b'''#: main.py:1 \xe2 \x81 \xa8 utils b.py\xe2 \x81 \xa9 :3
865+ msgid "foo"
866+ msgstr ""'''
867+
868+ def test_white_space_in_location_already_enclosed (self ):
869+ catalog = Catalog ()
870+ catalog .add ('foo' , locations = [('main.py' , 1 )])
871+ catalog .add ('foo' , locations = [('\u2068 utils b.py\u2069 ' , 3 )])
872+ buf = BytesIO ()
873+ pofile .write_po (buf , catalog , omit_header = True , include_lineno = True )
874+ assert buf .getvalue ().strip () == b'''#: main.py:1 \xe2 \x81 \xa8 utils b.py\xe2 \x81 \xa9 :3
875+ msgid "foo"
876+ msgstr ""'''
877+
878+ def test_tab_in_location (self ):
879+ catalog = Catalog ()
880+ catalog .add ('foo' , locations = [('main.py' , 1 )])
881+ catalog .add ('foo' , locations = [('utils\t b.py' , 3 )])
882+ buf = BytesIO ()
883+ pofile .write_po (buf , catalog , omit_header = True , include_lineno = True )
884+ assert buf .getvalue ().strip () == b'''#: main.py:1 \xe2 \x81 \xa8 utils b.py\xe2 \x81 \xa9 :3
885+ msgid "foo"
886+ msgstr ""'''
887+
888+ def test_tab_in_location_already_enclosed (self ):
889+ catalog = Catalog ()
890+ catalog .add ('foo' , locations = [('main.py' , 1 )])
891+ catalog .add ('foo' , locations = [('\u2068 utils\t b.py\u2069 ' , 3 )])
892+ buf = BytesIO ()
893+ pofile .write_po (buf , catalog , omit_header = True , include_lineno = True )
894+ assert buf .getvalue ().strip () == b'''#: main.py:1 \xe2 \x81 \xa8 utils b.py\xe2 \x81 \xa9 :3
895+ msgid "foo"
896+ msgstr ""'''
897+
898+
899+ class RoundtripPoTestCase (unittest .TestCase ):
900+
901+ def test_enclosed_filenames_in_location_comment (self ):
902+ catalog = Catalog ()
903+ catalog .add ("foo" , lineno = 2 , locations = [("main 1.py" , 1 )], string = "" )
904+ catalog .add ("bar" , lineno = 6 , locations = [("other.py" , 2 )], string = "" )
905+ catalog .add ("baz" , lineno = 10 , locations = [("main 1.py" , 3 ), ("other.py" , 4 )], string = "" )
906+ buf = BytesIO ()
907+ pofile .write_po (buf , catalog , omit_header = True , include_lineno = True )
908+ buf .seek (0 )
909+ catalog2 = pofile .read_po (buf )
910+ assert True is catalog .is_identical (catalog2 )
844911
845912class PofileFunctionsTestCase (unittest .TestCase ):
846913
@@ -864,6 +931,51 @@ def test_denormalize_on_msgstr_without_empty_first_line(self):
864931 assert expected_denormalized == pofile .denormalize (f'""\n { msgstr } ' )
865932
866933
934+ @pytest .mark .parametrize (("line" , "locations" ), [
935+ ("\u2068 file1.po\u2069 " , ["file1.po" ]),
936+ ("file1.po \u2068 file 2.po\u2069 file3.po" , ["file1.po" , "file 2.po" , "file3.po" ]),
937+ ("file1.po:1 \u2068 file 2.po\u2069 :2 file3.po:3" , ["file1.po:1" , "file 2.po:2" , "file3.po:3" ]),
938+ ("\u2068 file1.po\u2069 :1 \u2068 file\t 2.po\u2069 :2 file3.po:3" ,
939+ ["file1.po:1" , "file\t 2.po:2" , "file3.po:3" ]),
940+ ("file1.po file2.po" , ["file1.po" , "file2.po" ]),
941+ ("file1.po \u2068 \u2069 file2.po" , ["file1.po" , "file2.po" ]),
942+ ])
943+ def test_extract_locations_valid_location_comment (line , locations ):
944+ assert locations == _extract_locations (line )
945+
946+
947+ @pytest .mark .parametrize (("line" ,), [
948+ ("\u2068 file 1.po" ,),
949+ ("file 1.po\u2069 " ,),
950+ ("\u2069 file 1.po\u2068 " ,),
951+ ("\u2068 file 1.po:1 \u2068 file 2.po\u2069 :2" ,),
952+ ("\u2068 file 1.po\u2069 :1 file 2.po\u2069 :2" ,),
953+ ])
954+ def test_extract_locations_invalid_location_comment (line ):
955+ with pytest .raises (ValueError ):
956+ _extract_locations (line )
957+
958+
959+ @pytest .mark .parametrize (("filename" ,), [
960+ ("file.po" ,),
961+ ("file_a.po" ,),
962+ ("file-a.po" ,),
963+ ("file\n .po" ,),
964+ ("\u2068 file.po\u2069 " ,),
965+ ("\u2068 file a.po\u2069 " ,),
966+ ])
967+ def test_enclose_filename_if_necessary_no_change (filename ):
968+ assert filename == _enclose_filename_if_necessary (filename )
969+
970+
971+ @pytest .mark .parametrize (("filename" ,), [
972+ ("file a.po" ,),
973+ ("file\t a.po" ,),
974+ ])
975+ def test_enclose_filename_if_necessary_enclosed (filename ):
976+ assert "\u2068 " + filename + "\u2069 " == _enclose_filename_if_necessary (filename )
977+
978+
867979def test_unknown_language_roundtrip ():
868980 buf = StringIO (r'''
869981msgid ""
0 commit comments