Hello community,

here is the log from the commit of package python-avro for openSUSE:Factory 
checked in at 2019-09-16 10:50:25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-avro (Old)
 and      /work/SRC/openSUSE:Factory/.python-avro.new.7948 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-avro"

Mon Sep 16 10:50:25 2019 rev:4 rq:730689 version:1.9.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-avro/python-avro.changes  2019-06-22 
11:24:50.757427448 +0200
+++ /work/SRC/openSUSE:Factory/.python-avro.new.7948/python-avro.changes        
2019-09-16 10:50:28.787171632 +0200
@@ -1,0 +2,6 @@
+Fri Sep 13 11:26:49 UTC 2019 - Tomáš Chvátal <tchva...@suse.com>
+
+- Update to 1.9.0:
+  * no upstream changelog
+
+-------------------------------------------------------------------

Old:
----
  avro-1.9.0.tar.gz

New:
----
  avro-1.9.1.tar.gz

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

Other differences:
------------------
++++++ python-avro.spec ++++++
--- /var/tmp/diff_new_pack.9KxWoZ/_old  2019-09-16 10:50:29.823171498 +0200
+++ /var/tmp/diff_new_pack.9KxWoZ/_new  2019-09-16 10:50:29.827171497 +0200
@@ -18,19 +18,19 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-avro
-Version:        1.9.0
+Version:        1.9.1
 Release:        0
 Summary:        A serialization and RPC framework for Python
 License:        Apache-2.0
 Group:          Development/Languages/Python
-Url:            http://avro.apache.org/
+URL:            https://avro.apache.org/
 Source:         
https://files.pythonhosted.org/packages/source/a/avro/avro-%{version}.tar.gz
+BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Suggests:       python-python-snappy
 BuildArch:      noarch
-
 %python_subpackages
 
 %description
@@ -47,7 +47,9 @@
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %check
-%python_exec setup.py test
+# Dies on which is nowhere even in their VCS
+#  E   ImportError: No module named set_avro_test_path
+#%%pytest
 
 %files %{python_files}
 %python3_only %{_bindir}/avro

++++++ avro-1.9.0.tar.gz -> avro-1.9.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/PKG-INFO new/avro-1.9.1/PKG-INFO
--- old/avro-1.9.0/PKG-INFO     2019-05-21 16:59:01.000000000 +0200
+++ new/avro-1.9.1/PKG-INFO     2019-09-02 09:40:28.000000000 +0200
@@ -1,8 +1,8 @@
 Metadata-Version: 2.1
 Name: avro
-Version: 1.9.0
+Version: 1.9.1
 Summary: Avro is a serialization and RPC framework.
-Home-page: http://avro.apache.org/
+Home-page: https://avro.apache.org/
 Author: Apache Avro
 Author-email: d...@avro.apache.org
 License: Apache License 2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/avro.egg-info/PKG-INFO 
new/avro-1.9.1/avro.egg-info/PKG-INFO
--- old/avro-1.9.0/avro.egg-info/PKG-INFO       2019-05-21 16:59:01.000000000 
+0200
+++ new/avro-1.9.1/avro.egg-info/PKG-INFO       2019-09-02 09:40:28.000000000 
+0200
@@ -1,8 +1,8 @@
 Metadata-Version: 2.1
 Name: avro
-Version: 1.9.0
+Version: 1.9.1
 Summary: Avro is a serialization and RPC framework.
-Home-page: http://avro.apache.org/
+Home-page: https://avro.apache.org/
 Author: Apache Avro
 Author-email: d...@avro.apache.org
 License: Apache License 2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/avro.egg-info/SOURCES.txt 
new/avro-1.9.1/avro.egg-info/SOURCES.txt
--- old/avro-1.9.0/avro.egg-info/SOURCES.txt    2019-05-21 16:59:01.000000000 
+0200
+++ new/avro-1.9.1/avro.egg-info/SOURCES.txt    2019-09-02 09:40:28.000000000 
+0200
@@ -9,11 +9,13 @@
 src/avro/LICENSE
 src/avro/NOTICE
 src/avro/__init__.py
+src/avro/constants.py
 src/avro/datafile.py
 src/avro/io.py
 src/avro/ipc.py
 src/avro/protocol.py
 src/avro/schema.py
+src/avro/timezones.py
 src/avro/tool.py
 src/avro/txipc.py
 test/test_datafile.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/scripts/avro new/avro-1.9.1/scripts/avro
--- old/avro-1.9.0/scripts/avro 2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/scripts/avro 2019-08-28 11:35:18.000000000 +0200
@@ -8,7 +8,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -209,7 +209,7 @@
     argv = argv or sys.argv
 
     parser = OptionParser(description="Display/write for Avro files",
-                      version="1.9.0",
+                      version="1.9.1",
                       usage="usage: %prog cat|write [options] FILE [FILE...]")
     # cat options
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/setup.py new/avro-1.9.1/setup.py
--- old/avro-1.9.0/setup.py     2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/setup.py     2019-08-28 11:35:18.000000000 +0200
@@ -8,7 +8,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,7 +27,7 @@
 
 setup(
   name = 'avro',
-  version = '1.9.0',
+  version = '1.9.1',
   packages = ['avro',],
   package_dir = {'avro': 'src/avro'},
   scripts = ["./scripts/avro"],
@@ -45,7 +45,7 @@
   description = 'Avro is a serialization and RPC framework.',
   license = 'Apache License 2.0',
   keywords = 'avro serialization rpc',
-  url = 'http://avro.apache.org/',
+  url = 'https://avro.apache.org/',
   extras_require = {
     'snappy': ['python-snappy'],
   },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/LICENSE 
new/avro-1.9.1/src/avro/LICENSE
--- old/avro-1.9.0/src/avro/LICENSE     2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/LICENSE     2019-08-28 11:35:17.000000000 +0200
@@ -1,7 +1,7 @@
 
                                  Apache License
                            Version 2.0, January 2004
-                        http://www.apache.org/licenses/
+                        https://www.apache.org/licenses/
 
    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 
@@ -193,7 +193,7 @@
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
 
-       http://www.apache.org/licenses/LICENSE-2.0
+       https://www.apache.org/licenses/LICENSE-2.0
 
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/NOTICE 
new/avro-1.9.1/src/avro/NOTICE
--- old/avro-1.9.0/src/avro/NOTICE      2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/NOTICE      2019-08-28 11:35:17.000000000 +0200
@@ -2,5 +2,5 @@
 Copyright 2010-2015 The Apache Software Foundation
 
 This product includes software developed at
-The Apache Software Foundation (http://www.apache.org/).
+The Apache Software Foundation (https://www.apache.org/).
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/__init__.py 
new/avro-1.9.1/src/avro/__init__.py
--- old/avro-1.9.0/src/avro/__init__.py 2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/__init__.py 2019-08-28 11:35:17.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,5 +14,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-__all__ = ['schema', 'io', 'datafile', 'protocol', 'ipc']
+__all__ = ['schema', 'io', 'datafile', 'protocol', 'ipc', 'constants', 
'timezones']
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/constants.py 
new/avro-1.9.1/src/avro/constants.py
--- old/avro-1.9.0/src/avro/constants.py        1970-01-01 01:00:00.000000000 
+0100
+++ new/avro-1.9.1/src/avro/constants.py        2019-08-28 11:35:17.000000000 
+0200
@@ -0,0 +1,35 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+# https://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Contains Constants for Python Avro
+"""
+
+DATE = "date"
+DECIMAL = "decimal"
+TIMESTAMP_MICROS = "timestamp-micros"
+TIMESTAMP_MILLIS = "timestamp-millis"
+TIME_MICROS = "time-micros"
+TIME_MILLIS = "time-millis"
+
+SUPPORTED_LOGICAL_TYPE = [
+    DATE,
+    DECIMAL,
+    TIMESTAMP_MICROS,
+    TIMESTAMP_MILLIS,
+    TIME_MICROS,
+    TIME_MILLIS
+]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/datafile.py 
new/avro-1.9.1/src/avro/datafile.py
--- old/avro-1.9.0/src/avro/datafile.py 2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/datafile.py 2019-08-28 11:35:17.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/io.py 
new/avro-1.9.1/src/avro/io.py
--- old/avro-1.9.0/src/avro/io.py       2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/io.py       2019-08-28 11:35:17.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -38,8 +38,11 @@
 """
 import struct
 from avro import schema
+from avro import constants
+from avro import timezones
 import sys
 from binascii import crc32
+import datetime
 
 from decimal import Decimal
 from decimal import getcontext
@@ -104,6 +107,9 @@
 # Validate
 #
 
+def _is_timezone_aware_datetime(dt):
+  return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None
+
 def validate(expected_schema, datum):
   """Determine if a python datum is an instance of a schema."""
   schema_type = expected_schema.type
@@ -119,10 +125,20 @@
       return isinstance(datum, Decimal)
     return isinstance(datum, str)
   elif schema_type == 'int':
-    return ((isinstance(datum, int) or isinstance(datum, long)) 
+    if hasattr(expected_schema, 'logical_type'):
+      if expected_schema.logical_type == constants.DATE:
+        return isinstance(datum, datetime.date)
+      elif expected_schema.logical_type == constants.TIME_MILLIS:
+        return isinstance(datum, datetime.time)
+    return (isinstance(datum, (int, long))
             and INT_MIN_VALUE <= datum <= INT_MAX_VALUE)
   elif schema_type == 'long':
-    return ((isinstance(datum, int) or isinstance(datum, long)) 
+    if hasattr(expected_schema, 'logical_type'):
+      if expected_schema.logical_type == constants.TIME_MICROS:
+        return isinstance(datum, datetime.time)
+      elif expected_schema.logical_type in [constants.TIMESTAMP_MILLIS, 
constants.TIMESTAMP_MICROS]:
+        return isinstance(datum, datetime.datetime) and 
_is_timezone_aware_datetime(datum)
+    return (isinstance(datum, (int, long))
             and LONG_MIN_VALUE <= datum <= LONG_MAX_VALUE)
   elif schema_type in ['float', 'double']:
     return (isinstance(datum, int) or isinstance(datum, long)
@@ -280,6 +296,66 @@
     """
     return unicode(self.read_bytes(), "utf-8")
 
+  def read_date_from_int(self):
+    """
+    int is decoded as python date object.
+    int stores the number of days from
+    the unix epoch, 1 January 1970 (ISO calendar).
+    """
+    days_since_epoch = self.read_int()
+    return datetime.date(1970, 1, 1) + datetime.timedelta(days_since_epoch)
+
+  def _build_time_object(self, value, scale_to_micro):
+    value = value * scale_to_micro
+    value, microseconds =  value // 1000000, value % 1000000
+    value, seconds = value // 60, value % 60
+    value, minutes = value // 60, value % 60
+    hours = value
+
+    return datetime.time(
+      hour=hours,
+      minute=minutes,
+      second=seconds,
+      microsecond=microseconds
+    )
+
+  def read_time_millis_from_int(self):
+    """
+    int is decoded as python time object which represents 
+    the number of milliseconds after midnight, 00:00:00.000.
+    """
+    milliseconds = self.read_int()
+    return self._build_time_object(milliseconds, 1000)
+
+  def read_time_micros_from_long(self):
+    """
+    long is decoded as python time object which represents 
+    the number of microseconds after midnight, 00:00:00.000000.
+    """
+    microseconds = self.read_long()
+    return self._build_time_object(microseconds, 1)
+
+  def read_timestamp_millis_from_long(self):
+    """
+    long is decoded as python datetime object which represents 
+    the number of milliseconds from the unix epoch, 1 January 1970.
+    """
+    timestamp_millis = self.read_long()
+    timedelta = datetime.timedelta(microseconds=timestamp_millis * 1000)
+    unix_epoch_datetime = datetime.datetime(1970, 1, 1, 0, 0, 0, 0, 
tzinfo=timezones.utc) 
+    return unix_epoch_datetime + timedelta
+
+  def read_timestamp_micros_from_long(self):
+    """
+    long is decoded as python datetime object which represents 
+    the number of microseconds from the unix epoch, 1 January 1970.
+    """
+    timestamp_micros = self.read_long()
+    timedelta = datetime.timedelta(microseconds=timestamp_micros)
+    unix_epoch_datetime = datetime.datetime(1970, 1, 1, 0, 0, 0, 0, 
tzinfo=timezones.utc)
+    return unix_epoch_datetime + timedelta
+
+
   def check_crc32(self, bytes):
     checksum = STRUCT_CRC32.unpack(self.read(4))[0];
     if crc32(bytes) & 0xffffffff != checksum:
@@ -478,6 +554,56 @@
     """
     self.write(STRUCT_CRC32.pack(crc32(bytes) & 0xffffffff));
 
+  def write_date_int(self, datum):
+    """
+    Encode python date object as int.
+    It stores the number of days from
+    the unix epoch, 1 January 1970 (ISO calendar).
+    """
+    delta_date = datum - datetime.date(1970, 1, 1)
+    self.write_int(delta_date.days)
+
+  def write_time_millis_int(self, datum):
+    """
+    Encode python time object as int.
+    It stores the number of milliseconds from midnight, 00:00:00.000
+    """
+    milliseconds = datum.hour*3600000 + datum.minute * 60000 + datum.second * 
1000 + datum.microsecond // 1000
+    self.write_int(milliseconds)
+
+  def write_time_micros_long(self, datum):
+    """
+    Encode python time object as long.
+    It stores the number of microseconds from midnight, 00:00:00.000000
+    """
+    microseconds = datum.hour*3600000000 + datum.minute * 60000000 + 
datum.second * 1000000 + datum.microsecond
+    self.write_long(microseconds)
+
+  def _timedelta_total_microseconds(self, timedelta):
+    return (
+        timedelta.microseconds + (timedelta.seconds + timedelta.days * 24 * 
3600) * 10 ** 6)
+
+  def write_timestamp_millis_long(self, datum):
+    """
+    Encode python datetime object as long.
+    It stores the number of milliseconds from midnight of unix epoch, 1 
January 1970.
+    """
+    datum = datum.astimezone(tz=timezones.utc)
+    timedelta = datum - datetime.datetime(1970, 1, 1, 0, 0, 0, 0, 
tzinfo=timezones.utc)
+    milliseconds = self._timedelta_total_microseconds(timedelta) / 1000
+    self.write_long(long(milliseconds))
+
+  def write_timestamp_micros_long(self, datum):
+    """
+    Encode python datetime object as long.
+    It stores the number of microseconds from midnight of unix epoch, 1 
January 1970.
+    """
+    datum = datum.astimezone(tz=timezones.utc)
+    timedelta = datum - datetime.datetime(1970, 1, 1, 0, 0, 0, 0, 
tzinfo=timezones.utc)
+    microseconds = self._timedelta_total_microseconds(timedelta)
+    self.write_long(long(microseconds))
+
+
 #
 # DatumReader/Writer
 #
@@ -583,9 +709,26 @@
     elif writers_schema.type == 'string':
       return decoder.read_utf8()
     elif writers_schema.type == 'int':
-      return decoder.read_int()
+      if (hasattr(writers_schema, 'logical_type') and
+          writers_schema.logical_type == constants.DATE):
+        return decoder.read_date_from_int()
+      elif (hasattr(writers_schema, 'logical_type') and
+        writers_schema.logical_type == constants.TIME_MILLIS):
+        return decoder.read_time_millis_from_int()
+      else:
+        return decoder.read_int()
     elif writers_schema.type == 'long':
-      return decoder.read_long()
+      if (hasattr(writers_schema, 'logical_type') and 
+          writers_schema.logical_type == constants.TIME_MICROS):
+        return decoder.read_time_micros_from_long()
+      elif (hasattr(writers_schema, 'logical_type') and 
+            writers_schema.logical_type == constants.TIMESTAMP_MILLIS):
+        return decoder.read_timestamp_millis_from_long()
+      elif (hasattr(writers_schema, 'logical_type') and 
+            writers_schema.logical_type == constants.TIMESTAMP_MICROS):
+        return decoder.read_timestamp_micros_from_long()
+      else:
+        return decoder.read_long()
     elif writers_schema.type == 'float':
       return decoder.read_float()
     elif writers_schema.type == 'double':
@@ -909,9 +1052,26 @@
     elif writers_schema.type == 'string':
       encoder.write_utf8(datum)
     elif writers_schema.type == 'int':
-      encoder.write_int(datum)
+      if (hasattr(writers_schema, 'logical_type') and
+          writers_schema.logical_type == constants.DATE):
+        encoder.write_date_int(datum)
+      elif (hasattr(writers_schema, 'logical_type') and
+            writers_schema.logical_type == constants.TIME_MILLIS):
+        encoder.write_time_millis_int(datum)
+      else:
+        encoder.write_int(datum)
     elif writers_schema.type == 'long':
-      encoder.write_long(datum)
+      if (hasattr(writers_schema, 'logical_type') and
+          writers_schema.logical_type == constants.TIME_MICROS):
+        encoder.write_time_micros_long(datum)
+      elif (hasattr(writers_schema, 'logical_type') and
+            writers_schema.logical_type == constants.TIMESTAMP_MILLIS):
+        encoder.write_timestamp_millis_long(datum)
+      elif (hasattr(writers_schema, 'logical_type') and
+            writers_schema.logical_type == constants.TIMESTAMP_MICROS):
+        encoder.write_timestamp_micros_long(datum)
+      else:
+        encoder.write_long(datum)
     elif writers_schema.type == 'float':
       encoder.write_float(datum)
     elif writers_schema.type == 'double':
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/ipc.py 
new/avro-1.9.1/src/avro/ipc.py
--- old/avro-1.9.0/src/avro/ipc.py      2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/ipc.py      2019-08-28 11:35:18.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/protocol.py 
new/avro-1.9.1/src/avro/protocol.py
--- old/avro-1.9.0/src/avro/protocol.py 2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/protocol.py 2019-08-28 11:35:17.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/schema.py 
new/avro-1.9.1/src/avro/schema.py
--- old/avro-1.9.0/src/avro/schema.py   2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/schema.py   2019-08-28 11:35:17.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -41,6 +41,8 @@
 except ImportError:
   import simplejson as json
 
+from avro import constants
+
 #
 # Constants
 #
@@ -347,7 +349,7 @@
       raise SchemaParseException("Invalid DECIMAL scale %s. Cannot be greater 
than precision %s"
                                  %(scale, precision))
 
-    LogicalSchema.__init__(self, 'decimal')
+    super(DecimalLogicalSchema, self).__init__('decimal')
 
   def _max_precision(self):
     raise NotImplementedError()
@@ -788,6 +790,91 @@
     to_cmp = json.loads(str(self))
     return to_cmp == json.loads(str(that))
 
+
+#
+# Logical Type
+#
+
+class LogicalSchema(object):
+   def __init__(self, logical_type):
+     self.logical_type = logical_type
+
+
+#
+# Date Type
+#
+
+class DateSchema(LogicalSchema, PrimitiveSchema):
+  def __init__(self, other_props=None):
+    LogicalSchema.__init__(self, constants.DATE)
+    PrimitiveSchema.__init__(self, 'int', other_props)
+
+  def to_json(self, names=None):
+    return self.props
+
+  def __eq__(self, that):
+    return self.props == that.props
+
+#
+# time-millis Type
+#
+
+class TimeMillisSchema(LogicalSchema, PrimitiveSchema):
+  def __init__(self, other_props=None):
+    LogicalSchema.__init__(self, constants.TIME_MILLIS)
+    PrimitiveSchema.__init__(self, 'int', other_props)
+
+  def to_json(self, names=None):
+    return self.props
+
+  def __eq__(self, that):
+    return self.props == that.props
+
+#
+# time-micros Type
+#
+
+class TimeMicrosSchema(LogicalSchema, PrimitiveSchema):
+  def __init__(self, other_props=None):
+    LogicalSchema.__init__(self, constants.TIME_MICROS)
+    PrimitiveSchema.__init__(self, 'long', other_props)
+
+  def to_json(self, names=None):
+    return self.props
+
+  def __eq__(self, that):
+    return self.props == that.props
+
+#
+# timestamp-millis Type
+#
+
+class TimestampMillisSchema(LogicalSchema, PrimitiveSchema):
+  def __init__(self, other_props=None):
+    LogicalSchema.__init__(self, constants.TIMESTAMP_MILLIS)
+    PrimitiveSchema.__init__(self, 'long', other_props)
+
+  def to_json(self, names=None):
+    return self.props
+
+  def __eq__(self, that):
+    return self.props == that.props
+
+#
+# timestamp-micros Type
+#
+
+class TimestampMicrosSchema(LogicalSchema, PrimitiveSchema):
+  def __init__(self, other_props=None):
+    LogicalSchema.__init__(self, constants.TIMESTAMP_MICROS)
+    PrimitiveSchema.__init__(self, 'long', other_props)
+
+  def to_json(self, names=None):
+    return self.props
+
+  def __eq__(self, that):
+    return self.props == that.props
+
 #
 # Module Methods
 #
@@ -817,11 +904,20 @@
     logical_type = None
     if 'logicalType' in json_data:
       logical_type = json_data.get('logicalType')
-      if logical_type != 'decimal':
-       raise SchemaParseException("Currently does not support %s logical type" 
% logical_type)
+      if logical_type not in constants.SUPPORTED_LOGICAL_TYPE:
+        raise SchemaParseException("Currently does not support %s logical 
type" % logical_type)
     if type in PRIMITIVE_TYPES:
-      if type == 'bytes':
-        if logical_type == 'decimal':
+      if type == 'int' and logical_type == constants.DATE:
+        return DateSchema(other_props)
+      if type == 'int' and logical_type == constants.TIME_MILLIS:
+        return TimeMillisSchema(other_props=other_props)
+      if type == 'long' and logical_type == constants.TIME_MICROS:
+        return TimeMicrosSchema(other_props=other_props)
+      if type == 'long' and logical_type == constants.TIMESTAMP_MILLIS:
+        return TimestampMillisSchema(other_props=other_props)
+      if type == 'long' and logical_type == constants.TIMESTAMP_MICROS:
+        return TimestampMicrosSchema(other_props=other_props)
+      if type == 'bytes' and logical_type == constants.DECIMAL:
           precision = json_data.get('precision')
           scale = 0 if json_data.get('scale') is None else 
json_data.get('scale')
           return BytesDecimalSchema(precision, scale, other_props)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/timezones.py 
new/avro-1.9.1/src/avro/timezones.py
--- old/avro-1.9.0/src/avro/timezones.py        1970-01-01 01:00:00.000000000 
+0100
+++ new/avro-1.9.1/src/avro/timezones.py        2019-08-28 11:35:17.000000000 
+0200
@@ -0,0 +1,46 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+# https://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import datetime
+from datetime import timedelta
+from datetime import tzinfo
+
+
+class UTCTzinfo(tzinfo):
+  def utcoffset(self, dt):
+    return timedelta(0)
+
+  def tzname(self, dt):
+    return "UTC"
+
+  def dst(self, dt):
+    return timedelta(0)
+
+utc = UTCTzinfo()
+
+
+# Test Time Zone with fixed offset and no DST
+class TSTTzinfo(tzinfo):
+  def utcoffset(self, dt):
+    return timedelta(hours=10)
+
+  def tzname(self, dt):
+    return "TST"
+
+  def dst(self, dt):
+    return timedelta(0)
+
+tst = TSTTzinfo()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/tool.py 
new/avro-1.9.1/src/avro/tool.py
--- old/avro-1.9.0/src/avro/tool.py     2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/tool.py     2019-08-28 11:35:17.000000000 +0200
@@ -7,7 +7,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/src/avro/txipc.py 
new/avro-1.9.1/src/avro/txipc.py
--- old/avro-1.9.0/src/avro/txipc.py    2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/src/avro/txipc.py    2019-08-28 11:35:17.000000000 +0200
@@ -8,7 +8,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-#     http://www.apache.org/licenses/LICENSE-2.0
+#     https://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_datafile.py 
new/avro-1.9.1/test/test_datafile.py
--- old/avro-1.9.0/test/test_datafile.py        2019-05-21 15:54:34.000000000 
+0200
+++ new/avro-1.9.1/test/test_datafile.py        2019-08-28 11:35:17.000000000 
+0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_datafile_interop.py 
new/avro-1.9.1/test/test_datafile_interop.py
--- old/avro-1.9.0/test/test_datafile_interop.py        2019-05-21 
15:54:35.000000000 +0200
+++ new/avro-1.9.1/test/test_datafile_interop.py        2019-08-28 
11:35:18.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_io.py 
new/avro-1.9.1/test/test_io.py
--- old/avro-1.9.0/test/test_io.py      2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/test/test_io.py      2019-08-28 11:35:17.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -22,11 +22,13 @@
 except ImportError:
   from StringIO import StringIO
 from binascii import hexlify
+import datetime
 
 import set_avro_test_path
 
 from avro import schema
 from avro import io
+from avro import timezones
 
 SCHEMAS_TO_VALIDATE = (
   ('"null"', None),
@@ -48,6 +50,35 @@
   ('{"type": "array", "items": "long"}', [1, 3, 2]),
   ('{"type": "map", "values": "long"}', {'a': 1, 'b': 3, 'c': 2}),
   ('["string", "null", "long"]', None),
+  ('{"type": "int", "logicalType": "date"}', datetime.date(2000, 1, 1)),
+  ('{"type": "int", "logicalType": "time-millis"}', datetime.time(23, 59, 59, 
999000)),
+  ('{"type": "int", "logicalType": "time-millis"}', datetime.time(0, 0, 0, 
000000)),
+  ('{"type": "long", "logicalType": "time-micros"}', datetime.time(23, 59, 59, 
999999)),
+  ('{"type": "long", "logicalType": "time-micros"}', datetime.time(0, 0, 0, 
000000)),
+  (
+    '{"type": "long", "logicalType": "timestamp-millis"}',
+    datetime.datetime(1000, 1, 1, 0, 0, 0, 000000, tzinfo=timezones.utc)
+  ),
+  (
+    '{"type": "long", "logicalType": "timestamp-millis"}',
+    datetime.datetime(9999, 12, 31, 23, 59, 59, 999000, tzinfo=timezones.utc)
+  ),
+  (
+    '{"type": "long", "logicalType": "timestamp-millis"}',
+    datetime.datetime(2000, 1, 18, 2, 2, 1, 100000, tzinfo=timezones.tst)
+  ),
+  (
+    '{"type": "long", "logicalType": "timestamp-micros"}',
+    datetime.datetime(1000, 1, 1, 0, 0, 0, 000000, tzinfo=timezones.utc)
+  ),
+  (
+    '{"type": "long", "logicalType": "timestamp-micros"}',
+    datetime.datetime(9999, 12, 31, 23, 59, 59, 999999, tzinfo=timezones.utc)
+  ),
+  (
+    '{"type": "long", "logicalType": "timestamp-micros"}',
+    datetime.datetime(2000, 1, 18, 2, 2, 1, 123499, tzinfo=timezones.tst)
+  ),
   ("""\
    {"type": "record",
     "name": "Test",
@@ -211,7 +242,10 @@
       if isinstance(round_trip_datum, Decimal):
         round_trip_datum = round_trip_datum.to_eng_string()
         datum = str(datum)
-      if datum == round_trip_datum: correct += 1
+      elif isinstance(round_trip_datum, datetime.datetime):
+        datum = datum.astimezone(tz=timezones.utc)
+      if datum == round_trip_datum:
+        correct += 1
     self.assertEquals(correct, len(SCHEMAS_TO_VALIDATE))
 
   #
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_ipc.py 
new/avro-1.9.1/test/test_ipc.py
--- old/avro-1.9.0/test/test_ipc.py     2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/test/test_ipc.py     2019-08-28 11:35:17.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_protocol.py 
new/avro-1.9.1/test/test_protocol.py
--- old/avro-1.9.0/test/test_protocol.py        2019-05-21 15:54:34.000000000 
+0200
+++ new/avro-1.9.1/test/test_protocol.py        2019-08-28 11:35:17.000000000 
+0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_schema.py 
new/avro-1.9.1/test/test_schema.py
--- old/avro-1.9.0/test/test_schema.py  2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/test/test_schema.py  2019-08-28 11:35:17.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -313,6 +313,67 @@
   "scale": 2}""", True)
 ]
 
+DATE_LOGICAL_TYPE = [
+  ExampleSchema("""{
+  "type": "int",
+  "logicalType": "date"} """, True),
+  ExampleSchema("""{
+  "type": "int",
+  "logicalType": "date1"} """, False),
+  ExampleSchema("""{
+  "type": "long",
+  "logicalType": "date"} """, False),
+]
+
+TIMEMILLIS_LOGICAL_TYPE = [
+  ExampleSchema("""{
+  "type": "int",
+  "logicalType": "time-millis"} """, True),
+  ExampleSchema("""{
+  "type": "int",
+  "logicalType": "time-milis"} """, False),
+  ExampleSchema("""{
+  "type": "long",
+  "logicalType": "time-millis"} """, False),
+]
+
+TIMEMICROS_LOGICAL_TYPE = [
+  ExampleSchema("""{
+  "type": "long",
+  "logicalType": "time-micros"} """, True),
+  ExampleSchema("""{
+  "type": "long",
+  "logicalType": "time-micro"} """, False),
+  ExampleSchema("""{
+  "type": "int",
+  "logicalType": "time-micros"} """, False),
+]
+
+TIMESTAMPMILLIS_LOGICAL_TYPE = [
+  ExampleSchema("""{
+  "type": "long",
+  "logicalType": "timestamp-millis"} """, True),
+  ExampleSchema("""{
+  "type": "long",
+  "logicalType": "timestamp-milis"} """, False),
+  ExampleSchema("""{
+  "type": "int",
+  "logicalType": "timestamp-millis"} """, False),
+]
+
+TIMESTAMPMICROS_LOGICAL_TYPE = [
+  ExampleSchema("""{
+  "type": "long",
+  "logicalType": "timestamp-micros"} """, True),
+  ExampleSchema("""{
+  "type": "long",
+  "logicalType": "timestamp-micro"} """, False),
+  ExampleSchema("""{
+  "type": "int",
+  "logicalType": "timestamp-micros"} """, False),
+]
+
+
 EXAMPLES = PRIMITIVE_EXAMPLES
 EXAMPLES += FIXED_EXAMPLES
 EXAMPLES += ENUM_EXAMPLES
@@ -322,6 +383,11 @@
 EXAMPLES += RECORD_EXAMPLES
 EXAMPLES += DOC_EXAMPLES
 EXAMPLES += DECIMAL_LOGICAL_TYPE
+EXAMPLES += DATE_LOGICAL_TYPE
+EXAMPLES += TIMEMILLIS_LOGICAL_TYPE
+EXAMPLES += TIMEMICROS_LOGICAL_TYPE
+EXAMPLES += TIMESTAMPMILLIS_LOGICAL_TYPE
+EXAMPLES += TIMESTAMPMICROS_LOGICAL_TYPE
 
 VALID_EXAMPLES = [e for e in EXAMPLES if e.valid]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_script.py 
new/avro-1.9.1/test/test_script.py
--- old/avro-1.9.0/test/test_script.py  2019-05-21 15:54:34.000000000 +0200
+++ new/avro-1.9.1/test/test_script.py  2019-08-28 11:35:17.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 # 
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 # 
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_tether_task.py 
new/avro-1.9.1/test/test_tether_task.py
--- old/avro-1.9.0/test/test_tether_task.py     2019-05-21 15:54:34.000000000 
+0200
+++ new/avro-1.9.1/test/test_tether_task.py     2019-08-28 11:35:17.000000000 
+0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_tether_task_runner.py 
new/avro-1.9.1/test/test_tether_task_runner.py
--- old/avro-1.9.0/test/test_tether_task_runner.py      2019-05-21 
15:54:34.000000000 +0200
+++ new/avro-1.9.1/test/test_tether_task_runner.py      2019-08-28 
11:35:18.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/avro-1.9.0/test/test_tether_word_count.py 
new/avro-1.9.1/test/test_tether_word_count.py
--- old/avro-1.9.0/test/test_tether_word_count.py       2019-05-21 
15:54:36.000000000 +0200
+++ new/avro-1.9.1/test/test_tether_word_count.py       2019-08-28 
11:35:21.000000000 +0200
@@ -6,7 +6,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -146,7 +146,7 @@
 
       args.append("java")
       args.append("-jar")
-      
args.append(os.path.abspath("/avro/lang/py/../java/tools/target/avro-tools-1.9.0.jar"))
+      
args.append(os.path.abspath("/avro/lang/py/../java/tools/target/avro-tools-1.9.1.jar"))
 
 
       args.append("tether")


Reply via email to