Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package transifex-client for 
openSUSE:Factory checked in at 2022-04-27 21:41:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/transifex-client (Old)
 and      /work/SRC/openSUSE:Factory/.transifex-client.new.1538 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "transifex-client"

Wed Apr 27 21:41:12 2022 rev:18 rq:973025 version:0.14.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/transifex-client/transifex-client.changes        
2022-01-13 00:23:20.535972210 +0100
+++ 
/work/SRC/openSUSE:Factory/.transifex-client.new.1538/transifex-client.changes  
    2022-04-27 21:41:19.789013603 +0200
@@ -1,0 +2,23 @@
+Tue Apr 26 11:43:00 UTC 2022 - [email protected]
+
+- fix tests on 32 bit arch
+- added patches
+  fix https://github.com/transifex/transifex-client/pull/316
+  + transifex-client-fix-test-on-32bit.patch
+
+-------------------------------------------------------------------
+Tue Apr 26 11:30:16 UTC 2022 - [email protected]
+
+- version update to 0.14.4
+  * Patch issue 317
+  * Change download path when supplied language is source language
+  * Fix for CLI option of custom CA certificate bundle file
+  * Use authored date as git timestamp
+  * CLI option for custom CA certificate bundle file
+  * New flag to compare file timestamp through git and slugify update
+- do not require python-mock for build
+- added patches
+  fix https://github.com/transifex/transifex-client/pull/314
+  + transifex-client-no-mock.patch
+
+-------------------------------------------------------------------

Old:
----
  0.13.9.tar.gz

New:
----
  0.14.4.tar.gz
  transifex-client-fix-test-on-32bit.patch
  transifex-client-no-mock.patch

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ transifex-client.spec ++++++
--- /var/tmp/diff_new_pack.7Ce5Ui/_old  2022-04-27 21:41:20.489014428 +0200
+++ /var/tmp/diff_new_pack.7Ce5Ui/_new  2022-04-27 21:41:20.489014428 +0200
@@ -17,16 +17,20 @@
 
 
 Name:           transifex-client
-Version:        0.13.9
+Version:        0.14.4
 Release:        0
 Summary:        Transifex Command-line Client
 License:        GPL-2.0-only
 Group:          Productivity/Text/Utilities
 URL:            https://github.com/transifex/transifex-client
 Source:         
https://github.com/transifex/transifex-client/archive/%{version}.tar.gz
+# https://github.com/transifex/transifex-client/pull/314
+Patch0:         transifex-client-no-mock.patch
+# https://github.com/transifex/transifex-client/pull/316
+Patch1:         transifex-client-fix-test-on-32bit.patch
 BuildRequires:  python-rpm-macros
-BuildRequires:  python3-mock
-BuildRequires:  python3-mock >= 3.0.5
+BuildRequires:  python3-GitPython
+BuildRequires:  python3-pytest
 BuildRequires:  python3-python-slugify
 BuildRequires:  python3-requests >= 2.19.1
 BuildRequires:  python3-setuptools
@@ -52,8 +56,8 @@
 
 %prep
 %setup -q
-sed -i -e "s/slugify<.*/slugify/" requirements.txt
-sed -i -e "s/mock>=3.0.5,<4.0/mock>=3.0.5/" setup.py
+%patch0 -p1
+%patch1 -p1
 sed -i -e '1{\,^#!/usr/bin/env python,d}' txclib/cmdline.py
 
 %build
@@ -69,7 +73,8 @@
 ln -s -f %{_sysconfdir}/alternatives/tx %{buildroot}%{_bindir}/tx
 
 %check
-python3 -m unittest
+# test_fetch_timestamp_from_git_tree: this is not a git tree
+pytest -k 'not test_fetch_timestamp_from_git_tree'
 
 %post
 %{_sbindir}/update-alternatives --install %{_bindir}/tx tx 
%{_bindir}/transifex-tx 20

++++++ 0.13.9.tar.gz -> 0.14.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/.circleci/config.yml 
new/transifex-client-0.14.4/.circleci/config.yml
--- old/transifex-client-0.13.9/.circleci/config.yml    2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/.circleci/config.yml    2021-12-23 
11:34:36.000000000 +0100
@@ -5,8 +5,14 @@
     environment:
       CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
       CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
-      TOX_PY2: 2.7.13
-      TOX_PY3: 3.8.0
+      TOX_PY2: '2.7'
+      TOX_PY3: '3.9'
+      # NOTE(honles): The following are the official names, but point releases
+      # will likely be aliased as above pending either:
+      # - https://github.com/randomknowledge/docker-pyenv-tox/pull/6
+      # - https://github.com/randomknowledge/docker-pyenv-tox/pull/7
+      TOX_PY2_FALLBACK: 2.7.13
+      TOX_PY3_FALLBACK: 3.9.0
     # To see the list of pre-built images that CircleCI provides for most
     # common languages see
     # https://circleci.com/docs/2.0/circleci-images/
@@ -38,9 +44,24 @@
     # This is based on your 1.0 configuration file or project settings
     - run: pip -V
     - run: pip install -U ipdb
-    - run: pyenv install -s $TOX_PY2
-    - run: pyenv install -s $TOX_PY3
-    - run: pyenv local $TOX_PY2 $TOX_PY3
+    # allow installing pyenv versions not in the base image
+    - run: |
+        if ! pyenv install -s $TOX_PY2; then
+          TOX_PY2=$TOX_PY2_FALLBACK
+          PYENV_UPDATED=1
+          # update pyenv just in case
+          : $(cd /pyenv/ && git pull)
+          pyenv install -s $TOX_PY2
+        fi
+
+        if ! pyenv install -s $TOX_PY3; then
+          TOX_PY3=$TOX_PY3_FALLBACK
+          # if not already updated, update pyenv
+          test -z "$PYENV_UPDATED" && : $(cd /pyenv/ && git pull)
+          pyenv install -s $TOX_PY3
+        fi
+
+        pyenv local $TOX_PY2 $TOX_PY3
     # Save dependency cache
     - save_cache:
         key: txclient-{{ .Branch }}-{{ epoch }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/README.md 
new/transifex-client-0.14.4/README.md
--- old/transifex-client-0.13.9/README.md       2020-04-07 15:17:40.000000000 
+0200
+++ new/transifex-client-0.14.4/README.md       2021-12-23 11:34:36.000000000 
+0100
@@ -5,7 +5,7 @@
 
[![codecov](https://codecov.io/gh/transifex/transifex-client/branch/master/graph/badge.svg)](https://codecov.io/gh/transifex/transifex-client)
 [![PyPI 
version](https://badge.fury.io/py/transifex-client.svg)](https://badge.fury.io/py/transifex-client)
 
-
+> _?????? A new client, compatible with the [new 
API](https://transifex.github.io/openapi/), is under development. Check out the 
Alpha version [here](https://github.com/transifex/cli)._
 
 ## Getting started
 Whether you have experience with the command line or not, [this interactive 
tutorial](https://www.transifex.com/learn/txclient/) is intended for everyone 
who wishes to learn how the Transifex client works. There is no need to 
download anything - Just click on the link provided above, and follow the 
instructions.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/appveyor.yml 
new/transifex-client-0.14.4/appveyor.yml
--- old/transifex-client-0.13.9/appveyor.yml    2020-04-07 15:17:40.000000000 
+0200
+++ new/transifex-client-0.14.4/appveyor.yml    2021-12-23 11:34:36.000000000 
+0100
@@ -48,7 +48,7 @@
   # Upgrade to the latest version of pip to avoid it displaying warnings
   # about it being out of date.
   - "python -m pip install --disable-pip-version-check --user --upgrade pip"
-  - "python -m pip install --no-use-pep517 pyinstaller==3.4"
+  - "python -m pip install --no-use-pep517 pyinstaller==3.6"
 
   # Set up the project in develop mode. If some dependencies contain
   # compiled extensions and are not provided as pre-built wheel packages,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/contrib/tx.spec 
new/transifex-client-0.14.4/contrib/tx.spec
--- old/transifex-client-0.13.9/contrib/tx.spec 2020-04-07 15:17:40.000000000 
+0200
+++ new/transifex-client-0.14.4/contrib/tx.spec 2021-12-23 11:34:36.000000000 
+0100
@@ -26,4 +26,4 @@
           debug=False,
           strip=None,
           upx=False,
-          console=True , icon='contrib/tx.ico')
+          console=True , icon='tx.ico')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/requirements.txt 
new/transifex-client-0.14.4/requirements.txt
--- old/transifex-client-0.13.9/requirements.txt        2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/requirements.txt        2021-12-23 
11:34:36.000000000 +0100
@@ -1,4 +1,5 @@
 urllib3>=1.24.2,<2.0.0
 six<2.0.0
 requests>=2.19.1,<3.0.0
-python-slugify<2.0.0
+python-slugify<5.0.0
+gitpython<4.0.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/setup.py 
new/transifex-client-0.14.4/setup.py
--- old/transifex-client-0.13.9/setup.py        2020-04-07 15:17:40.000000000 
+0200
+++ new/transifex-client-0.14.4/setup.py        2021-12-23 11:34:36.000000000 
+0100
@@ -24,7 +24,7 @@
     license="GPLv2",
     dependency_links=[],
     setup_requires=[],
-    python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,<3.9",
+    python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,<3.10",
     install_requires=get_file_content("requirements.txt").splitlines(),
     tests_require=["mock>=3.0.5,<4.0"],
     data_files=[],
@@ -42,5 +42,6 @@
         "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
     ],
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/transifex-client-0.13.9/tests/project_dir/.transifexrc 
new/transifex-client-0.14.4/tests/project_dir/.transifexrc
--- old/transifex-client-0.13.9/tests/project_dir/.transifexrc  2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/tests/project_dir/.transifexrc  2021-12-23 
11:34:36.000000000 +0100
@@ -3,4 +3,3 @@
 hostname = https://www.transifex.com
 password = foo
 username = bar
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/tests/test_commands.py 
new/transifex-client-0.14.4/tests/test_commands.py
--- old/transifex-client-0.13.9/tests/test_commands.py  2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/tests/test_commands.py  2021-12-23 
11:34:36.000000000 +0100
@@ -81,7 +81,7 @@
 
     def test_init(self):
         argv = []
-        config_text = "[main]\nhost = https://www.transifex.com\n\n";
+        config_text = "[main]\nhost = https://www.transifex.com\n";
         with patch('txclib.commands.project.Project'):
             with patch('txclib.commands.cmd_config') as set_mock:
                 cmd_init(argv, '')
@@ -163,7 +163,8 @@
             branch='a-branch', fetchall=False, fetchsource=False,
             force=False, languages=[], minimum_perc=None, mode=None,
             overwrite=True, pseudo=False, resources=[], skip=False,
-            xliff=False, parallel=False, no_interactive=False
+            xliff=False, parallel=False, no_interactive=False,
+            use_git_timestamps=False
         )
         pr_instance.pull.assert_has_calls([pull_call])
 
@@ -182,7 +183,8 @@
             branch='somebranch', fetchall=False, fetchsource=False,
             force=False, languages=[], minimum_perc=None, mode=None,
             overwrite=True, pseudo=False, resources=[], skip=False,
-            xliff=False, parallel=False, no_interactive=False
+            xliff=False, parallel=False, no_interactive=False,
+            use_git_timestamps=False
         )
         pr_instance.pull.assert_has_calls([pull_call])
 
@@ -196,7 +198,24 @@
             fetchall=False, force=False, minimum_perc=None,
             skip=False, no_interactive=True, resources=[], pseudo=False,
             languages=[], fetchsource=False, mode=None, branch=None,
-            xliff=False, parallel=False, overwrite=True
+            xliff=False, parallel=False, overwrite=True,
+            use_git_timestamps=False
+        )
+        self.assertEqual(pr_instance.pull.call_count, 1)
+        pr_instance.pull.assert_has_calls([pull_call])
+
+    @patch('txclib.commands.project')
+    def test_pull_with_git_timestamps(self, project_mock):
+        pr_instance = MagicMock()
+        pr_instance.pull.return_value = True
+        project_mock.Project.return_value = pr_instance
+        cmd_pull(['--use-git-timestamps'], '.')
+        pull_call = call(
+            fetchall=False, force=False, minimum_perc=None,
+            skip=False, no_interactive=False, resources=[], pseudo=False,
+            languages=[], fetchsource=False, mode=None, branch=None,
+            xliff=False, parallel=False, overwrite=True,
+            use_git_timestamps=True
         )
         self.assertEqual(pr_instance.pull.call_count, 1)
         pr_instance.pull.assert_has_calls([pull_call])
@@ -236,7 +255,7 @@
 
     def test_bare_set_source_file(self):
         expected = ("[main]\nhost = https://foo.var\n\n[project1.resource1]\n";
-                    "source_file = test.txt\nsource_lang = en\n\n")
+                    "source_file = test.txt\nsource_lang = en\n")
         args = ["-r", "project1.resource1", '--source', '-l', 'en', 'test.txt']
         cmd_config(args, self.path_to_tx)
         with open(self.config_file) as config:
@@ -245,7 +264,7 @@
         # set translation file for de
         expected = ("[main]\nhost = https://foo.var\n\n[project1.resource1]\n";
                     "source_file = test.txt\nsource_lang = en\n"
-                    "trans.de = translations/de.txt\n\n")
+                    "trans.de = translations/de.txt\n")
         args = ["-r", "project1.resource1", '-l', 'de', 'translations/de.txt']
         cmd_config(args, self.path_to_tx)
         with open(self.config_file) as config:
@@ -270,7 +289,7 @@
         expected = ("[main]\nhost = https://foo.var\n\n[project1.resource1]\n";
                     "file_filter = translations/<lang>/test.txt\n"
                     "source_file = translations/en/test.txt\n"
-                    "source_lang = en\n\n")
+                    "source_lang = en\n")
 
         args = ["--auto-local", "-r", "project1.resource1",
                 '--source-language', 'en', '--execute',
@@ -283,7 +302,7 @@
         expected = ("[main]\nhost = https://foo.var\n\n[project1.resource1]\n";
                     "file_filter = translations/<lang>/test.txt\n"
                     "source_file = translations/en/test.txt\n"
-                    "source_lang = en\n\n")
+                    "source_lang = en\n")
 
         args = [MAPPING, "-r", "project1.resource1", '--source-language',
                 'en', '--execute', 'translations/<lang>/test.txt']
@@ -314,7 +333,7 @@
                     "file_filter = translations/proj.resource_1/<lang>.txt\n"
                     "source_lang = fr\ntype = TXT\n\n[proj.resource_2]\n"
                     "file_filter = translations/proj.resource_2/<lang>.txt\n"
-                    "source_lang = fr\ntype = TXT\n\n")
+                    "source_lang = fr\ntype = TXT\n")
         extension_mock.return_value = ".txt"
         get_details_mock.side_effect = [
             # project details
@@ -353,7 +372,7 @@
                     "file_filter = translations/proj.resource_1/<lang>.txt\n"
                     "source_lang = fr\ntype = TXT\n\n[proj.resource_2]\n"
                     "file_filter = translations/proj.resource_2/<lang>.txt\n"
-                    "source_lang = fr\ntype = TXT\n\n")
+                    "source_lang = fr\ntype = TXT\n")
         extension_mock.return_value = ".txt"
         get_details_mock.side_effect = [
             # project details
@@ -398,7 +417,7 @@
                     "[test-project.translations_en_test]\n"
                     "file_filter = translations/<lang>/en/test.txt\n"
                     "source_file = translations/en/test.txt\n"
-                    "source_lang = en\ntype = TXT\n\n")
+                    "source_lang = en\ntype = TXT\n")
         args = [MAPPINGBULK, "-p", "test-project", "--source-file-dir",
                 "translations", "--source-language", "en", "-t", "TXT",
                 "--file-extension", ".txt", "--execute", "--expression",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/tests/test_project.py 
new/transifex-client-0.14.4/tests/test_project.py
--- old/transifex-client-0.13.9/tests/test_project.py   2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/tests/test_project.py   2021-12-23 
11:34:36.000000000 +0100
@@ -704,6 +704,27 @@
                 res = self.p._should_download('pt', self.stats, None, True)
                 self.assertEqual(res, True)
 
+            with patch.object(utils, 'get_git_file_timestamp') as ts_mock:
+                # Note that the test needs an existing file path
+                # The current test (__file__) is the only file
+                # we're sure will exist and won't change
+
+                # Old timestamp (1990)
+                ts_mock.return_value = 640124371
+                res = self.p._should_download(
+                    'pt', self.stats, os.path.abspath(__file__), False,
+                    use_git_timestamps=True
+                )
+                self.assertEqual(res, True)
+
+                # "Recent" timestamp (in the future - 2100)
+                ts_mock.return_value = 4111417171
+                res = self.p._should_download(
+                    'pt', self.stats, os.path.abspath(__file__), False,
+                    use_git_timestamps=True
+                )
+                self.assertEqual(res, False)
+
     def test_get_url_by_pull_mode(self):
         self.assertEqual(
             'pull_sourceastranslation_file',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/tests/test_utils.py 
new/transifex-client-0.14.4/tests/test_utils.py
--- old/transifex-client-0.13.9/tests/test_utils.py     2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/tests/test_utils.py     2021-12-23 
11:34:36.000000000 +0100
@@ -1,7 +1,8 @@
 import os
+import time
 import unittest
 import six
-from mock import patch, MagicMock, mock_open
+from mock import patch, MagicMock, Mock, mock_open
 from urllib3.exceptions import SSLError
 
 from txclib import utils, exceptions
@@ -364,3 +365,31 @@
         with six.assertRaisesRegex(self, exceptions.MalformedConfigFile, msg):
             for file, lang in utils.get_project_files(os.getcwd(), expression):
                 pass
+
+class GitUtilsTestCase(unittest.TestCase):
+
+    def test_fetch_timestamp_from_git_tree(self):
+        # A bit meta, lets try to get the timestamp of the
+        # current file
+        epoch_ts = utils.get_git_file_timestamp(
+            os.path.dirname(os.path.abspath(__file__))
+        )
+        self.assertIsNotNone(epoch_ts)
+
+    def test_git_timestamp_is_parsable(self):
+        # A bit meta, lets try to get the timestamp of the
+        # current file
+        epoch_ts = utils.get_git_file_timestamp(
+            os.path.dirname(os.path.abspath(__file__))
+        )
+        parsed_ts = time.mktime(time.gmtime(epoch_ts))
+        self.assertIsNotNone(parsed_ts)
+
+    def test_uses_authorized_date(self):
+        commit = Mock()
+        commit.authored_date = 1590969254
+        commit.committed_date = 1590970456
+        with patch('txclib.utils.git.Repo') as repo:
+            repo.return_value.iter_commits.return_value = [commit]
+            epoch_ts = utils.get_git_file_timestamp('any')
+        self.assertEqual(1590969254, epoch_ts)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/tox.ini 
new/transifex-client-0.14.4/tox.ini
--- old/transifex-client-0.13.9/tox.ini 2020-04-07 15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/tox.ini 2021-12-23 11:34:36.000000000 +0100
@@ -1,5 +1,5 @@
 [tox]
-envlist = py{27,38}-{vanilla,pyopenssl}
+envlist = py{27,39}-{vanilla,pyopenssl}
 
 [testenv]
 deps =
@@ -14,4 +14,4 @@
 commands = python -V
            coverage run setup.py test
            bash ./contrib/test_build.sh
-           bash -c 'test -n "$CI" && codecov -e TOX_ENV_NAME'
+           bash -c 'if [ -n "$CI" ]; then codecov -e TOX_ENV_NAME; fi'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/txclib/__init__.py 
new/transifex-client-0.14.4/txclib/__init__.py
--- old/transifex-client-0.13.9/txclib/__init__.py      2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/txclib/__init__.py      2021-12-23 
11:34:36.000000000 +0100
@@ -1,4 +1,4 @@
 # -*- coding: utf-8 -*-
 
 # 
https://www.python.org/dev/peps/pep-0440/#examples-of-compliant-version-schemes
-__version__ = '0.13.9'
+__version__ = '0.14.4'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/txclib/cmdline.py 
new/transifex-client-0.14.4/txclib/cmdline.py
--- old/transifex-client-0.13.9/txclib/cmdline.py       2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/txclib/cmdline.py       2021-12-23 
11:34:36.000000000 +0100
@@ -4,7 +4,7 @@
 import sys
 from urllib3.exceptions import SSLError
 
-from txclib import utils
+from txclib import utils, web
 from txclib.log import set_log_level, logger
 from txclib.parsers import tx_main_parser
 from txclib.exceptions import AuthenticationError
@@ -62,6 +62,8 @@
     elif options.debug:
         set_log_level('DEBUG')
 
+    web.cacerts_file = options.cacert
+
     # find .tx
     path_to_tx = options.root_dir or utils.find_dot_tx()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/txclib/commands.py 
new/transifex-client-0.14.4/txclib/commands.py
--- old/transifex-client-0.13.9/txclib/commands.py      2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/txclib/commands.py      2021-12-23 
11:34:36.000000000 +0100
@@ -414,6 +414,7 @@
     skip = options.skip_errors
     xliff = options.xliff
     parallel = options.parallel
+    use_git_timestamps = options.use_git_timestamps
     prj = project.Project(path_to_tx)
     if not (options.push_source or options.push_translations):
         parser.error("You need to specify at least one of the -s|--source, "
@@ -425,7 +426,8 @@
         skip=skip, source=options.push_source,
         translations=options.push_translations,
         no_interactive=options.no_interactive,
-        xliff=xliff, branch=branch, parallel=parallel
+        xliff=xliff, branch=branch, parallel=parallel,
+        use_git_timestamps=use_git_timestamps,
     )
     logger.info("Done.")
 
@@ -445,6 +447,7 @@
     parallel = options.parallel
     skip = options.skip_errors
     minimum_perc = options.minimum_perc or None
+    use_git_timestamps = options.use_git_timestamps
 
     _go_to_dir(path_to_tx)
 
@@ -457,6 +460,7 @@
         force=options.force, skip=skip, minimum_perc=minimum_perc,
         mode=options.mode, pseudo=pseudo, xliff=xliff, branch=branch,
         parallel=parallel, no_interactive=options.no_interactive,
+        use_git_timestamps=use_git_timestamps
     )
     logger.info("Done.")
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/txclib/config.py 
new/transifex-client-0.14.4/txclib/config.py
--- old/transifex-client-0.13.9/txclib/config.py        2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/txclib/config.py        2021-12-23 
11:34:36.000000000 +0100
@@ -14,20 +14,21 @@
     """
     def write(self, fp):
         """Write an .ini-format representation of the configuration state."""
+        section_prefix = ''
         if self._defaults:
             fp.write("[%s]\n" % DEFAULTSECT)
             for key in sorted(self._defaults):
                 fp.write("%s = %s\n" % (key, str(self._defaults[key]).
                          replace('\n', '\n\t')))
-            fp.write("\n")
+            section_prefix = '\n'
         for section in self._sections:
-            fp.write("[%s]\n" % section)
+            fp.write("%s[%s]\n" % (section_prefix, section))
             for key in sorted(self._sections[section]):
                 if key != "__name__":
                     fp.write("%s = %s\n" %
                              (key, str(self._sections[section][key]).
                               replace('\n', '\n\t')))
-            fp.write("\n")
+            section_prefix = '\n'
 
     optionxform = str
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/txclib/parsers.py 
new/transifex-client-0.14.4/txclib/parsers.py
--- old/transifex-client-0.13.9/txclib/parsers.py       2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/txclib/parsers.py       2021-12-23 
11:34:36.000000000 +0100
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 
+import argparse
 import os
 import sys
 
@@ -11,6 +12,12 @@
     'mapping', 'mapping-remote', 'mapping-bulk'
 )
 
+def check_file_exists(file=None):
+    if file and not os.path.isfile(file):
+        raise argparse.ArgumentTypeError(
+            'certificate file %s not found' % file)
+    return file
+
 
 def tx_main_parser():
     description = "This is the Transifex command line client which"\
@@ -46,6 +53,12 @@
         default=(os.name == 'nt' or not sys.stdout.isatty()),
         help="disable colors in the output of commands"
     )
+    # set a private CA cert bundle file to override the system one
+    parser.add_argument(
+        "--cacert", action="store", dest="cacert", default=None,
+        help="set path to CA certificate bundle file",
+        metavar='/path/to/ca-cert-bundle-file', type=check_file_exists
+    )
     parser.add_argument(
         "command", action="store", help="TX command", nargs='?', default=None
     )
@@ -182,6 +195,11 @@
     parser.add_argument("--pseudo", action="store_true", dest="pseudo",
                         default=False, help="Apply this option to download "
                         "a pseudo file.")
+    parser.add_argument("--use-git-timestamps", action="store_true",
+                        dest="use_git_timestamps", default=False,
+                        help="Compare local files to their Transifex version "
+                        "by their latest commit timestamps. Use this option, "
+                        "for example, when cloning a Git repository.")
     parser.add_argument(
         "--mode", action="store", dest="mode", help=(
             "Specify the mode of the translation file to pull (e.g. "
@@ -244,6 +262,11 @@
     parser.add_argument("-x", "--xliff", action="store_true", dest="xliff",
                         default=False, help="Apply this option to upload "
                         "file as xliff.")
+    parser.add_argument("--use-git-timestamps", action="store_true",
+                        dest="use_git_timestamps", default=False,
+                        help="Compare local files to their Transifex version "
+                        "by their latest commit timestamps. Use this option, "
+                        "for example, when cloning a Git repository.")
     parser.add_argument(
         "-b", "--branch", action="store", dest="branch",
         default=None, nargs="?", const='-1',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/txclib/project.py 
new/transifex-client-0.14.4/txclib/project.py
--- old/transifex-client-0.13.9/txclib/project.py       2020-04-07 
15:17:40.000000000 +0200
+++ new/transifex-client-0.14.4/txclib/project.py       2021-12-23 
11:34:36.000000000 +0100
@@ -394,7 +394,7 @@
     def pull(self, languages=None, resources=None, overwrite=True,
              fetchall=False, fetchsource=False, force=False, skip=False,
              minimum_perc=0, mode=None, pseudo=False, xliff=False, branch=None,
-             parallel=False, no_interactive=False):
+             parallel=False, no_interactive=False, use_git_timestamps=False):
         """Pull all translations file from Transifex server."""
         languages = languages or []
         resources = resources or []
@@ -460,7 +460,9 @@
                 pseudo_file = self._get_pseudo_file(
                     slang, resource, file_filter
                 )
-                if self._should_download(slang, stats, local_file=pseudo_file):
+                if self._should_download(
+                        slang, stats, local_file=pseudo_file,
+                        use_git_timestamps=use_git_timestamps):
                     logger.info("Pulling pseudo file for resource %s (%s)." % (
                         resource,
                         utils.color_text(pseudo_file, "RED")
@@ -473,7 +475,7 @@
 
             if fetchall:
                 new_translations = self._new_translations_to_add(
-                    files, slang, lang_map, stats, force
+                    files, slang, lang_map, stats, force, use_git_timestamps
                 )
                 if new_translations:
                     msg = ("New translations found "
@@ -487,7 +489,9 @@
             new_translations |= new
             logger.debug("Adding to new translations: %s" % new)
 
-            if fetchsource:
+            if fetchsource or slang in languages:
+                new_translations.discard(slang)
+                pull_languages.discard(slang)
                 if sfile and slang not in pull_languages:
                     pull_languages.add(slang)
                 elif slang not in new_translations:
@@ -527,6 +531,7 @@
                     'local_file': local_file,
                     'force': force,
                     'mode': mode,
+                    'use_git_timestamps': use_git_timestamps
                 }
 
                 # xliff files should be always pulled
@@ -611,7 +616,7 @@
 
     def push(self, source=False, translations=False, force=False,
              resources=None, languages=None, skip=False, no_interactive=False,
-             xliff=False, branch=None, parallel=False):
+             xliff=False, branch=None, parallel=False, 
use_git_timestamps=False):
         """Push all the resources"""
         languages = languages or []
         resources = resources or []
@@ -744,6 +749,7 @@
                         'stats': stats,
                         'local_file': local_file,
                         'force': force,
+                        'use_git_timestamps': use_git_timestamps,
                     }
                     if not self._should_push_translation(**kwargs):
                         msg = "Skipping '%s' translation (file: %s)."
@@ -987,7 +993,7 @@
         )
 
     def _should_update_translation(self, lang, stats, local_file, force=False,
-                                   mode=None):
+                                   mode=None, use_git_timestamps=False):
         """Whether a translation should be udpated from Transifex.
 
         We use the following criteria for that:
@@ -1002,12 +1008,15 @@
             local_file: The local translation file.
             force: A boolean flag.
             mode: The mode for the translation.
+            use_git_timestamps: Boolean flag -  use latest commit timestamp
+                instead of system timestamps for checking if local file is
+                older than Transifex's.
         Returns:
             True or False.
         """
-        return self._should_download(lang, stats, local_file, force)
+        return self._should_download(lang, stats, local_file, force, 
use_git_timestamps=use_git_timestamps)
 
-    def _should_add_translation(self, lang, stats, force=False, mode=None):
+    def _should_add_translation(self, lang, stats, force=False, mode=None, 
use_git_timestamps=False):
         """Whether a translation should be added from Transifex.
 
         We use the following criteria for that:
@@ -1020,13 +1029,16 @@
             stats: The (global) statistics object.
             force: A boolean flag.
             mode: The mode for the translation.
+            use_git_timestamps: Boolean flag -  use latest commit timestamp
+                instead of system timestamps for checking if local file is
+                older than Transifex's.
         Returns:
             True or False.
         """
-        return self._should_download(lang, stats, None, force)
+        return self._should_download(lang, stats, None, force, 
use_git_timestamps=use_git_timestamps)
 
     def _should_download(self, lang, stats, local_file=None, force=False,
-                         mode=None):
+                         mode=None, use_git_timestamps=False):
         """Return whether a translation should be downloaded.
 
         If local_file is None, skip the timestamps check (the file does
@@ -1048,12 +1060,12 @@
 
         if local_file is not None:
             remote_update = self._extract_updated(lang_stats)
-            if not self._remote_is_newer(remote_update, local_file):
+            if not self._remote_is_newer(remote_update, local_file, 
use_git_timestamps):
                 logger.debug("Local is newer than remote for lang %s" % lang)
                 return False
         return True
 
-    def _should_push_translation(self, lang, stats, local_file, force=False):
+    def _should_push_translation(self, lang, stats, local_file, force=False, 
use_git_timestamps=False):
         """Return whether a local translation file should be
         pushed to Trasnifex.
 
@@ -1067,6 +1079,9 @@
             stats: The (global) statistics object.
             local_file: The local translation file.
             force: A boolean flag.
+            use_git_timestamps: Boolean flag -  use latest commit timestamp
+                instead of system timestamps for checking if local file is
+                older than Transifex's.
         Returns:
             True or False.
         """
@@ -1080,7 +1095,7 @@
             return True
         if local_file is not None:
             remote_update = self._extract_updated(lang_stats)
-            if self._remote_is_newer(remote_update, local_file):
+            if self._remote_is_newer(remote_update, local_file, 
use_git_timestamps):
                 msg = "Remote translation is newer than local file for lang %s"
                 logger.debug(msg % lang)
                 return False
@@ -1102,16 +1117,29 @@
             ).utctimetuple()
         )
 
-    def _get_time_of_local_file(self, path):
+    def _get_time_of_local_file(self, path, use_git_timestamps=False):
         """Get the modified time of the path_.
 
         Args:
             path: The path we want the mtime for.
+            use_git_timestamps: Boolean flag -  use latest commit timestamp
+                instead of system timestamps for checking if local file is
+                older than Transifex's.
         Returns:
             The time as a timestamp or None, if the file does not exist
         """
         if not os.path.exists(path):
             return None
+
+        if use_git_timestamps:
+            epoch_timestamp = utils.get_git_file_timestamp(path)
+            if epoch_timestamp:
+                return time.mktime(time.gmtime(epoch_timestamp))
+            else:
+                logger.warning(
+                    "Failed to find git repository. Fallback to OS timstamp"
+                )
+
         return time.mktime(time.gmtime(os.path.getmtime(path)))
 
     def _satisfies_min_translated(self, stats, mode=None):
@@ -1139,13 +1167,16 @@
             minimum_percent = resource_minimum
         return cur >= minimum_percent
 
-    def _remote_is_newer(self, remote_updated, local_file):
+    def _remote_is_newer(self, remote_updated, local_file, 
use_git_timestamps=False):
         """Check whether the remote translation is newer that the local file.
 
         Args:
             remote_updated: The date and time the translation was last
                 updated remotely.
             local_file: The local file.
+            use_git_timestamps: Boolean flag -  use latest commit timestamp
+                instead of system timestamps for checking if local file is
+                older than Transifex's.
         Returns:
             True or False.
         """
@@ -1154,7 +1185,8 @@
             return False
         remote_time = self._generate_timestamp(remote_updated)
         local_time = self._get_time_of_local_file(
-            self.get_full_path(local_file)
+            self.get_full_path(local_file),
+            use_git_timestamps
         )
         logger.debug(
             "Remote time is %s and local %s" % (remote_time, local_time)
@@ -1211,7 +1243,7 @@
             fd.write(response['content'].encode("utf-8"))
 
     def _new_translations_to_add(self, files, slang, lang_map,
-                                 stats, force=False):
+                                 stats, force=False, use_git_timestamps=False):
         """Return a list of translations which are
         new to the local installation.
         """
@@ -1227,7 +1259,7 @@
             )
             if lang_exists or lang_is_source or mapped_lang_exists:
                 continue
-            if self._should_add_translation(lang, stats, force):
+            if self._should_add_translation(lang, stats, force, 
use_git_timestamps):
                 new_translations.append(lang)
         return set(new_translations)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/txclib/utils.py 
new/transifex-client-0.14.4/txclib/utils.py
--- old/transifex-client-0.13.9/txclib/utils.py 2020-04-07 15:17:40.000000000 
+0200
+++ new/transifex-client-0.14.4/txclib/utils.py 2021-12-23 11:34:36.000000000 
+0100
@@ -3,6 +3,7 @@
 import os
 import sys
 import re
+import git
 import errno
 import urllib3
 import collections
@@ -733,3 +734,23 @@
     # Print a new line when done
     if done == total:
         sys.stdout.write('\n')
+
+
+def get_git_file_timestamp(file_path):
+    """
+    Return the timestamp (epoch) for the latest commit of a file
+    """
+    try:
+        repo = git.Repo()
+        commits_touching_path = list(
+            repo.iter_commits(paths=file_path, max_count=1)
+        )
+        if commits_touching_path:
+            latest_commit = commits_touching_path[0]
+            latest_commit_ts = latest_commit.authored_date
+            return latest_commit_ts
+        else:
+            return None
+    except git.InvalidGitRepositoryError:
+        # Current path is not a git repo.
+        return None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transifex-client-0.13.9/txclib/web.py 
new/transifex-client-0.14.4/txclib/web.py
--- old/transifex-client-0.13.9/txclib/web.py   2020-04-07 15:17:40.000000000 
+0200
+++ new/transifex-client-0.14.4/txclib/web.py   2021-12-23 11:34:36.000000000 
+0100
@@ -6,6 +6,8 @@
 import txclib
 
 
+cacerts_file = None
+
 def user_agent_identifier():
     """Return the user agent for the client."""
     client_info = (txclib.__version__, platform.system(), platform.machine())
@@ -13,6 +15,10 @@
 
 
 def certs_file():
+    return cacerts_file or system_certs_file()
+
+
+def system_certs_file():
     if platform.system() == 'Windows':
         return os.path.join(txclib.utils.get_base_dir(), 'cacert.pem')
     else:

++++++ transifex-client-fix-test-on-32bit.patch ++++++
Index: transifex-client-0.14.4/tests/test_project.py
===================================================================
--- transifex-client-0.14.4.orig/tests/test_project.py
+++ transifex-client-0.14.4/tests/test_project.py
@@ -720,8 +720,8 @@ class TestProjectPull(unittest.TestCase)
                 )
                 self.assertEqual(res, True)
 
-                # "Recent" timestamp (in the future - 2100)
-                ts_mock.return_value = 4111417171
+                # "Recent" timestamp (in the future - 2038)
+                ts_mock.return_value = 2147483000
                 res = self.p._should_download(
                     'pt', self.stats, os.path.abspath(__file__), False,
                     use_git_timestamps=True

++++++ transifex-client-no-mock.patch ++++++
diff --git a/setup.py b/setup.py
index 8d6028e5..89b26e0a 100755
--- a/setup.py
+++ b/setup.py
@@ -26,7 +26,7 @@ def get_file_content(filename):
     setup_requires=[],
     python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,<3.10",
     install_requires=get_file_content("requirements.txt").splitlines(),
-    tests_require=["mock>=3.0.5,<4.0"],
+    tests_require=["mock>=3.0.5,<4.0; python_version < '3.3'"],
     data_files=[],
     test_suite="tests",
     zip_safe=False,
diff --git a/tests/test_api.py b/tests/test_api.py
index 554da392..01c4c5ef 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -1,7 +1,10 @@
 # -*- coding: utf-8 -*-
 
 import unittest
-from mock import MagicMock, patch
+try:
+    from unittest.mock import MagicMock, patch
+except ImportError:
+    from mock import MagicMock, patch
 
 from txclib.api import Api
 
diff --git a/tests/test_commands.py b/tests/test_commands.py
index b2e4bbd9..062d29c0 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -7,7 +7,10 @@
     from StringIO import StringIO
 except ImportError:
     from io import StringIO
-from mock import patch, MagicMock, call
+try:
+    from unittest.mock import patch, MagicMock, call
+except ImportError:
+    from mock import patch, MagicMock, call
 from six import assertRaisesRegex
 from txclib.commands import _set_source_file, _set_translation, cmd_pull, \
     cmd_init, cmd_config, cmd_status, cmd_help, UnInitializedError
diff --git a/tests/test_paths.py b/tests/test_paths.py
index 09702805..14d6abda 100644
--- a/tests/test_paths.py
+++ b/tests/test_paths.py
@@ -2,7 +2,10 @@
 
 import os
 import unittest
-from mock import patch
+try:
+    from unittest.mock import patch
+except ImportError:
+    from mock import patch
 from txclib.paths import posix_path, native_path
 
 
diff --git a/tests/test_project.py b/tests/test_project.py
index 1d90dd86..0986bbca 100644
--- a/tests/test_project.py
+++ b/tests/test_project.py
@@ -13,7 +13,10 @@
     import ConfigParser as configparser
 
 from functools import wraps
-from mock import Mock, patch, mock_open
+try:
+    from unittest.mock import Mock, patch, mock_open
+except ImportError:
+    from mock import Mock, patch, mock_open
 from collections import namedtuple
 from os.path import dirname
 from sys import modules, version_info
diff --git a/tests/test_utils.py b/tests/test_utils.py
index a605cf6d..40b26125 100755
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -2,7 +2,10 @@
 import time
 import unittest
 import six
-from mock import patch, MagicMock, Mock, mock_open
+try:
+    from unittest.mock import patch, MagicMock, Mock, mock_open
+except ImportError:
+    from mock import patch, MagicMock, Mock, mock_open
 from urllib3.exceptions import SSLError
 
 from txclib import utils, exceptions
diff --git a/tests/test_wizard.py b/tests/test_wizard.py
index bfed3d0f..03286822 100644
--- a/tests/test_wizard.py
+++ b/tests/test_wizard.py
@@ -1,7 +1,10 @@
 # -*- coding: utf-8 -*-
 
 import unittest
-from mock import patch, MagicMock
+try:
+    from unittest.mock import patch, MagicMock
+except ImportError:
+    from mock import patch, MagicMock
 
 from six import assertRaisesRegex
 

Reply via email to