mafrosis/jira-offline

tzlocal > 2.1 causes failures

Opened this issue · 1 comments

If I unpin tzlocal , or set it to any version higher than 2.1, there are a lot of test failures. Compatibility with the latest versions of dependencies will allow jira-offline to be packages as a tool on Linux distros, similar to how you have a brew tap.

Very likely this is due to the deprecation of pytz occurring @ https://pypi.org/project/tzlocal/ and https://pypi.org/project/pytz-deprecation-shim/

I note that pytz is explicitly used in jira-offline , but has been omitted from requirements.txt

test_cli_commands_can_return_json[edit-params2] ―

mock_jira = <jira_offline.jira.Jira object at 0x7f1cf776e610>
project = ProjectMeta(key='TEST', name=None, username='test', password='dummy', protocol='https', hostname='jira.atlassian.com',...", "Story Done", "Epic Done", "Closed")'), issue=UserConfig.Issue(board_id={}, default_reporter={}), customfields={})))
command = 'edit', params = ('--json', 'TEST-71', '--summary', 'A new summary')

    @pytest.mark.parametrize('command,params', [
        ('show', ('--json', 'TEST-71')),
        ('new', ('--json', 'TEST', 'Story', 'Summary of issue')),
        ('edit', ('--json', 'TEST-71', '--summary', 'A new summary')),
    ])
    def test_cli_commands_can_return_json(mock_jira, project, command, params):
        '''
        Ensure show command can return output as JSON
        '''
        # add a single lonely fixture to the Jira store
        mock_jira['TEST-71'] = Issue.deserialize(ISSUE_1, project)
    
        runner = CliRunner(mix_stderr=False)
    
        with mock.patch('jira_offline.cli.main.jira', mock_jira), \
                mock.patch('jira_offline.cli.utils.jira', mock_jira), \
                mock.patch('jira_offline.create.jira', mock_jira), \
                mock.patch('jira_offline.jira.jira', mock_jira):
            result = runner.invoke(cli, [command, *params])
    
>       assert result.exit_code == 0, result.output
E       AssertionError: 
E       assert 1 == 0
E        +  where 1 = <Result TypeError('Cannot convert tz-naive Timestamp, use tz_localize to localize')>.exit_code
test/models/test_issue.py ⨯✓                                                                                                                                                                                                     

― test_issue_model__to_series_from_series_roundtripproject = ProjectMeta(key='TEST', name=None, username='test', password='dummy', protocol='https', hostname='jira.atlassian.com',...id='10000', default_reporter=None, board_id=None, sprints={1: Sprint(id=1, name='Sprint 1', active=True)}, config=None)

    def test_issue_model__to_series_from_series_roundtrip(project):
        '''
        Ensure that Issue.to_series and Issue.from_series are behaving
        '''
        issue_1 = Issue.deserialize(ISSUE_1, project)
    
        roundtrip_issue = Issue.from_series(issue_1.to_series(), project)
    
>       compare_issue_helper(issue_1, roundtrip_issue)

test/models/test_issue.py:161: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

issue = Issue(project_id='99fd9182cfc4c701a8a662f6293f4136201791b4', issuetype='Story', summary='This is the story summary', k..., epic_name=None, sprint=None, story_points=Decimal('1'), parent_link=None, extended={}, modified=None, transitions={})
compare_issue = Issue(project_id='99fd9182cfc4c701a8a662f6293f4136201791b4', issuetype='Story', summary='This is the story summary', k...epic_name=None, sprint=None, story_points=Decimal('1'), parent_link=None, extended={}, modified=None, transitions=None)

    def compare_issue_helper(issue, compare_issue):
        'Helper to compare two issues with assertions'
        assert isinstance(compare_issue, Issue)
        assert issue.project == compare_issue.project
        assert issue.project_id == compare_issue.project_id
        assert issue.issuetype == compare_issue.issuetype
        assert issue.summary == compare_issue.summary
        assert issue.assignee == compare_issue.assignee
>       assert issue.created == compare_issue.created
E       AssertionError

test/helpers.py:15: AssertionError
----------------------------------------------------------------------------------------------------------- Captured stderr call ------------------------------------------------------------------------------------------------------------
Traceback (most recent call last):
  File "pandas/_libs/tslibs/timezones.pyx", line 266, in pandas._libs.tslibs.timezones.get_dst_info
AttributeError: 'NoneType' object has no attribute 'total_seconds'
Traceback (most recent call last):
  File "pandas/_libs/tslibs/timezones.pyx", line 266, in pandas._libs.tslibs.timezones.get_dst_info
AttributeError: 'NoneType' object has no attribute 'total_seconds'

I very quickly tried the following patch based on the suggestion at https://pypi.org/project/pytz-deprecation-shim/ , but it doesnt solve these problems.

diff --git a/jira_offline/jira.py b/jira_offline/jira.py
index 0c02b10..1dabb9d 100644
--- a/jira_offline/jira.py
+++ b/jira_offline/jira.py
@@ -12,7 +12,7 @@ from typing import cast, Dict, Hashable, List, Optional, Set
 import numpy
 import pandas as pd
 from peak.util.proxies import LazyProxy
-import pytz
+import pytz_deprecation_shim as pds
 
 from jira_offline.config import get_cache_filepath, load_config
 from jira_offline.config.user_config import apply_user_config_to_project
@@ -374,7 +374,7 @@ class Jira(collections.abc.MutableMapping):
             # Pull user's configured timezone from their profile and store on the ProjectMeta
             tz = self._get_user_timezone(project)
             if tz is not None:
-                project.timezone = pytz.timezone(tz)
+                project.timezone = pds.timezone(tz)
 
             # Retrieve the list of available sprints on this project
             self._get_sprints(project)
diff --git a/jira_offline/models.py b/jira_offline/models.py
index d239174..b3b8c12 100644
--- a/jira_offline/models.py
+++ b/jira_offline/models.py
@@ -18,7 +18,7 @@ import dictdiffer
 import numpy
 from oauthlib.oauth1 import SIGNATURE_RSA
 import pandas as pd
-import pytz
+import pytz_deprecation_shim as pds
 from requests.auth import HTTPBasicAuth
 from requests_oauthlib import OAuth1
 from rich import box
@@ -198,7 +198,7 @@ class ProjectMeta(DataclassSerializer):
             key=uri.path[1:],
             protocol=uri.scheme,
             hostname=uri.netloc,
-            timezone=pytz.timezone(timezone) if timezone else get_localzone(),
+            timezone=pds.timezone(timezone) if timezone else get_localzone(),
         )
 
     def set_ca_cert(self, ca_cert: str):
diff --git a/jira_offline/utils/serializer.py b/jira_offline/utils/serializer.py
index 432f20d..330cb29 100644
--- a/jira_offline/utils/serializer.py
+++ b/jira_offline/utils/serializer.py
@@ -8,7 +8,7 @@ from typing import Any, cast, Dict, Hashable, List, Optional, Tuple, TYPE_CHECKI
 import uuid
 
 import arrow
-import pytz
+import pytz_deprecation_shim as pds
 import typing_inspect
 from tzlocal import get_localzone
 
@@ -162,7 +162,7 @@ def deserialize_value(type_, value: Any, tz: datetime.tzinfo, project: Optional[
             raise DeserializeError(f'Failed deserializing "{value}" to Arrow datetime.datetime')
 
     elif base_type is datetime.tzinfo:
-        return pytz.timezone(value)
+        return pds.timezone(value)
 
     elif base_type is int:
         try:
diff --git a/test/conftest.py b/test/conftest.py
index d9d41a5..492e658 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -7,7 +7,7 @@ from unittest import mock
 import docker
 import pandas as pd
 import pytest
-import pytz
+import pytz_deprecation_shim as pds
 from requests.auth import HTTPBasicAuth
 import requests
 
@@ -43,7 +43,7 @@ def project():
 
 @pytest.fixture(params=[
     get_localzone(),
-    pytz.timezone('America/New_York'),  # UTC-5
+    pds.timezone('America/New_York'),  # UTC-5
 ])
 def timezone_project(request, project):
     '''

Hi @jayvdb, thanks for your interest in this project. I haven't been working on it for some time now - it never really gained any momentum in the community or with the handful of users I know that tried it.

For what distro are you looking to package, out of interest? It might not be worth your time, given my statement above.