mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(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:
@@ -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()]
|
||||
|
||||
@@ -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,))
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user