(core) Simple Python 3 compatibility changes

Summary: Changes that move towards python 3 compatibility that are easy to review without much thought

Test Plan: The tests

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2873
This commit is contained in:
Alex Hall
2021-06-22 17:12:25 +02:00
parent cc04c6481a
commit 16f297a250
66 changed files with 551 additions and 437 deletions

View File

@@ -1,12 +1,12 @@
# pylint: disable=wildcard-import
from date import *
from info import *
from logical import *
from lookup import *
from math import *
from stats import *
from text import *
from schedule import *
from .date import *
from .info import *
from .logical import *
from .lookup import *
from .math import *
from .stats import *
from .text import *
from .schedule import *
# Export all uppercase names, for use with `from functions import *`.
__all__ = [k for k in dir() if not k.startswith('_') and k.isupper()]

View File

@@ -1,6 +1,8 @@
import calendar
import datetime
import dateutil.parser
import six
import moment
import docmodel
@@ -16,7 +18,7 @@ def _make_datetime(value):
return datetime.datetime.combine(value, datetime.time())
elif isinstance(value, datetime.time):
return datetime.datetime.combine(datetime.date.today(), value)
elif isinstance(value, basestring):
elif isinstance(value, six.string_types):
return dateutil.parser.parse(value)
else:
raise ValueError('Invalid date %r' % (value,))

View File

@@ -7,6 +7,8 @@ import math
import numbers
import re
import six
import column
from functions import date # pylint: disable=import-error
from functions.unimplemented import unimplemented
@@ -217,7 +219,7 @@ def ISTEXT(value):
>>> ISTEXT(datetime.date(2011, 1, 1))
False
"""
return isinstance(value, (basestring, AltText))
return isinstance(value, (six.string_types, AltText))
# Regexp for matching email. See ISEMAIL for justification.

View File

@@ -1,4 +1,4 @@
from info import lazy_value_or_error, is_error
from .info import lazy_value_or_error, is_error
from usertypes import AltText # pylint: disable=unused-import,import-error
@@ -63,7 +63,7 @@ def IF(logical_expression, value_if_true, value_if_false):
0.0
More tests:
>>> IF(True, lambda: (1/0), lambda: (17))
>>> IF(True, lambda: (1/0), lambda: (17)) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ZeroDivisionError: integer division or modulo by zero

View File

@@ -1,9 +1,10 @@
# pylint: disable=redefined-builtin, line-too-long
from collections import OrderedDict
import os
from urllib import urlencode
import urlparse
from unimplemented import unimplemented
import six
from six.moves import urllib_parse
from .unimplemented import unimplemented
@unimplemented
def ADDRESS(row, column, absolute_relative_mode, use_a1_notation, sheet):
@@ -112,16 +113,16 @@ def SELF_HYPERLINK(label=None, page=None, **kwargs):
if page:
txt += "/p/{}".format(page)
if kwargs:
parts = list(urlparse.urlparse(txt))
query = OrderedDict(urlparse.parse_qsl(parts[4]))
for [key, value] in kwargs.iteritems():
parts = list(urllib_parse.urlparse(txt))
query = OrderedDict(urllib_parse.parse_qsl(parts[4]))
for [key, value] in sorted(six.iteritems(kwargs)):
key_parts = key.split('LinkKey_')
if len(key_parts) == 2 and key_parts[0] == '':
query[key_parts[1] + '_'] = value
else:
raise TypeError("unexpected keyword argument '{}' (not of form LinkKey_NAME)".format(key))
parts[4] = urlencode(query)
txt = urlparse.urlunparse(parts)
parts[4] = urllib_parse.urlencode(query)
txt = urllib_parse.urlunparse(parts)
if label:
txt = "{} {}".format(label, txt)
return txt

View File

@@ -1,12 +1,15 @@
# pylint: disable=unused-argument
from __future__ import absolute_import
import itertools
import math as _math
import operator
import os
import random
import uuid
from functools import reduce
from six.moves import zip, xrange
import six
from functions.info import ISNUMBER, ISLOGICAL
from functions.unimplemented import unimplemented
@@ -358,7 +361,7 @@ def INT(value):
return int(_math.floor(value))
def _lcm(a, b):
return a * b / _gcd(a, b)
return a * b // _gcd(a, b)
def LCM(value1, *more_values):
"""
@@ -790,7 +793,7 @@ def SUMPRODUCT(array1, *more_arrays):
>>> SUMPRODUCT([-0.25, -0.25], [-2, -2], [-3, -3])
-3.0
"""
return sum(reduce(operator.mul, values) for values in itertools.izip(array1, *more_arrays))
return sum(reduce(operator.mul, values) for values in zip(array1, *more_arrays))
@unimplemented
def SUMSQ(value1, value2):
@@ -842,4 +845,7 @@ def TRUNC(value, places=0):
def UUID():
"""Generate a random UUID-formatted string identifier."""
return str(uuid.UUID(bytes=[chr(random.randrange(0, 256)) for _ in xrange(0, 16)], version=4))
if six.PY2:
return str(uuid.UUID(bytes=[chr(random.randrange(0, 256)) for _ in xrange(0, 16)], version=4))
else:
return str(uuid.UUID(bytes=bytes([random.randrange(0, 256) for _ in range(0, 16)]), version=4))

View File

@@ -1,6 +1,6 @@
from datetime import datetime, timedelta
import re
from date import DATEADD, NOW, DTIME
from .date import DATEADD, NOW, DTIME
from moment_parse import MONTH_NAMES, DAY_NAMES
# Limit exports to schedule, so that upper-case constants like MONTH_NAMES, DAY_NAMES don't end up

View File

@@ -1,9 +1,9 @@
# pylint: disable=redefined-builtin, line-too-long, unused-argument
from math import _chain, _chain_numeric, _chain_numeric_a
from info import ISNUMBER, ISLOGICAL
from date import DATE # pylint: disable=unused-import
from unimplemented import unimplemented
from .math import _chain, _chain_numeric, _chain_numeric_a
from .info import ISNUMBER, ISLOGICAL
from .date import DATE # pylint: disable=unused-import
from .unimplemented import unimplemented
def _average(iterable):
total, count = 0.0, 0
@@ -326,7 +326,7 @@ def MEDIAN(value, *more_values):
3
>>> MEDIAN(3, 5, 1, 4, 2)
3
>>> MEDIAN(xrange(10))
>>> MEDIAN(range(10))
4.5
>>> MEDIAN("Hello", "123", DATE(2015, 1, 1), 12.3)
12.3
@@ -340,9 +340,9 @@ def MEDIAN(value, *more_values):
raise ValueError("MEDIAN requires at least one number")
count = len(values)
if count % 2 == 0:
return (values[count / 2 - 1] + values[count / 2]) / 2.0
return (values[count // 2 - 1] + values[count // 2]) / 2.0
else:
return values[(count - 1) / 2]
return values[(count - 1) // 2]
def MIN(value, *more_values):

View File

@@ -4,7 +4,7 @@ import timeit
import unittest
import moment
import schedule
from . import schedule
from functions.date import DTIME
from functions import date as _date
@@ -68,7 +68,7 @@ class TestSchedule(unittest.TestCase):
self.assertDate(RDU(DT("2018-09-04 14:38:11"), "seconds"), "2018-09-04 14:38:11")
self.assertDate(RDU(DT("2018-09-04 14:38:11") - TICK, "seconds"), "2018-09-04 14:38:10")
with self.assertRaisesRegexp(ValueError, r"Invalid unit inches"):
with self.assertRaisesRegex(ValueError, r"Invalid unit inches"):
RDU(DT("2018-09-04 14:38:11"), "inches")
def test_round_down_to_unit_tz(self):
@@ -99,11 +99,11 @@ class TestSchedule(unittest.TestCase):
self.assertEqual(schedule._parse_interval("25-months"), (25, "months"))
self.assertEqual(schedule._parse_interval("3-day"), (3, "days"))
self.assertEqual(schedule._parse_interval("2-hour"), (2, "hours"))
with self.assertRaisesRegexp(ValueError, "Not a valid interval"):
with self.assertRaisesRegex(ValueError, "Not a valid interval"):
schedule._parse_interval("1Year")
with self.assertRaisesRegexp(ValueError, "Not a valid interval"):
with self.assertRaisesRegex(ValueError, "Not a valid interval"):
schedule._parse_interval("1y")
with self.assertRaisesRegexp(ValueError, "Unknown unit"):
with self.assertRaisesRegex(ValueError, "Unknown unit"):
schedule._parse_interval("1-daily")
def test_parse_slot(self):
@@ -145,41 +145,41 @@ class TestSchedule(unittest.TestCase):
def test_parse_slot_errors(self):
# Test failures with duplicate units
with self.assertRaisesRegexp(ValueError, 'Duplicate unit'):
with self.assertRaisesRegex(ValueError, 'Duplicate unit'):
schedule._parse_slot('+1d +2d', 'weeks')
with self.assertRaisesRegexp(ValueError, 'Duplicate unit'):
with self.assertRaisesRegex(ValueError, 'Duplicate unit'):
schedule._parse_slot('9:30am +2H', 'days')
with self.assertRaisesRegexp(ValueError, 'Duplicate unit'):
with self.assertRaisesRegex(ValueError, 'Duplicate unit'):
schedule._parse_slot('/15 +1d', 'months')
with self.assertRaisesRegexp(ValueError, 'Duplicate unit'):
with self.assertRaisesRegex(ValueError, 'Duplicate unit'):
schedule._parse_slot('Feb-1 12:30pm +20M', 'years')
# Test failures with improper slot types
with self.assertRaisesRegexp(ValueError, 'Invalid slot.*for unit'):
with self.assertRaisesRegex(ValueError, 'Invalid slot.*for unit'):
schedule._parse_slot('Feb-1', 'weeks')
with self.assertRaisesRegexp(ValueError, 'Invalid slot.*for unit'):
with self.assertRaisesRegex(ValueError, 'Invalid slot.*for unit'):
schedule._parse_slot('Monday', 'months')
with self.assertRaisesRegexp(ValueError, 'Invalid slot.*for unit'):
with self.assertRaisesRegex(ValueError, 'Invalid slot.*for unit'):
schedule._parse_slot('4/15', 'hours')
with self.assertRaisesRegexp(ValueError, 'Invalid slot.*for unit'):
with self.assertRaisesRegex(ValueError, 'Invalid slot.*for unit'):
schedule._parse_slot('/1', 'years')
# Test failures with outright invalid slot syntax.
with self.assertRaisesRegexp(ValueError, 'Invalid slot'):
with self.assertRaisesRegex(ValueError, 'Invalid slot'):
schedule._parse_slot('Feb:1', 'weeks')
with self.assertRaisesRegexp(ValueError, 'Invalid slot'):
with self.assertRaisesRegex(ValueError, 'Invalid slot'):
schedule._parse_slot('/1d', 'months')
with self.assertRaisesRegexp(ValueError, 'Invalid slot'):
with self.assertRaisesRegex(ValueError, 'Invalid slot'):
schedule._parse_slot('10', 'hours')
with self.assertRaisesRegexp(ValueError, 'Invalid slot'):
with self.assertRaisesRegex(ValueError, 'Invalid slot'):
schedule._parse_slot('H1', 'years')
# Test failures with unknown values
with self.assertRaisesRegexp(ValueError, 'Unknown month'):
with self.assertRaisesRegex(ValueError, 'Unknown month'):
schedule._parse_slot('februarium-1', 'years')
with self.assertRaisesRegexp(ValueError, 'Unknown day of the week'):
with self.assertRaisesRegex(ValueError, 'Unknown day of the week'):
schedule._parse_slot('snu', 'weeks')
with self.assertRaisesRegexp(ValueError, 'Unknown unit'):
with self.assertRaisesRegex(ValueError, 'Unknown unit'):
schedule._parse_slot('+1t', 'hours')
def test_schedule(self):
@@ -250,14 +250,14 @@ from datetime import datetime
]
self.assertEqual(timing_schedule_full(), expected_result)
t = min(timeit.repeat(stmt="t.timing_schedule_full()", setup=setup, number=N, repeat=3))
print "\n*** SCHEDULE call with 4 points: %.2f us" % (t * 1000000 / N)
print("\n*** SCHEDULE call with 4 points: %.2f us" % (t * 1000000 / N))
t = min(timeit.repeat(stmt="t.timing_schedule_init()", setup=setup, number=N, repeat=3))
print "*** Schedule constructor: %.2f us" % (t * 1000000 / N)
print("*** Schedule constructor: %.2f us" % (t * 1000000 / N))
self.assertEqual(timing_schedule_series(), expected_result)
t = min(timeit.repeat(stmt="t.timing_schedule_series()", setup=setup, number=N, repeat=3))
print "*** Schedule series with 4 points: %.2f us" % (t * 1000000 / N)
print("*** Schedule series with 4 points: %.2f us" % (t * 1000000 / N))
def timing_schedule_full():
return list(schedule.SCHEDULE("weekly: Mo 10:30am, We 10:30pm",

View File

@@ -5,7 +5,11 @@ import dateutil.parser
import numbers
import re
from unimplemented import unimplemented
import six
from six import unichr
from six.moves import xrange
from .unimplemented import unimplemented
from usertypes import AltText # pylint: disable=import-error
def CHAR(table_number):
@@ -478,7 +482,7 @@ def SUBSTITUTE(text, old_text, new_text, instance_num=None):
if not old_text:
return text
if not isinstance(new_text, basestring):
if not isinstance(new_text, six.string_types):
new_text = str(new_text)
if instance_num is None: