Skip to content

Commit

Permalink
Remove leftover support for Python 2
Browse files Browse the repository at this point in the history
Also update cmstestsuite.sh() description, as it no longer matched the
implementation.
  • Loading branch information
andreyv committed Oct 10, 2020
1 parent 6676e58 commit 75e4041
Show file tree
Hide file tree
Showing 19 changed files with 38 additions and 129 deletions.
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ For Python code, we generally follow [PEP 8](https://www.python.org/dev/peps/pep
We get around Python flexible type system in several ways:
* we try to avoid "magic" (e.g., generating or changing classes on the fly);
* we are fairly verbose with naming, trying to help the reader with following the types;
* we follow our type annotation system for method and function docstrings (planning to switch to [PEP 484](https://www.python.org/dev/peps/pep-0484/) when we will remove support for Python 2); see later for the format.
* we follow our type annotation system for method and function docstrings (planning to switch to [PEP 484](https://www.python.org/dev/peps/pep-0484/)); see later for the format.

We support Python 3 only, requiring at least version 3.6.

Expand Down
3 changes: 0 additions & 3 deletions cms/db/filecacher.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@ def copyfileobj(source_fobj, destination_fobj,
while len(buffer) > 0:
gevent.sleep(0)
written = destination_fobj.write(buffer)
# FIXME remove this when we drop py2
if written is None:
break
buffer = buffer[written:]
gevent.sleep(0)

Expand Down
10 changes: 5 additions & 5 deletions cms/grading/Sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import select
import stat
import tempfile
import time
from abc import ABCMeta, abstractmethod
from functools import wraps, partial

Expand All @@ -34,7 +35,6 @@

from cms import config, rmtree
from cmscommon.commands import pretty_print_cmdline
from cmscommon.datetime import monotonic_time


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -594,7 +594,7 @@ def get_execution_wall_clock_time(self):
if self.exec_time:
return self.exec_time
if self.popen_time:
self.exec_time = monotonic_time() - self.popen_time
self.exec_time = time.monotonic() - self.popen_time
return self.exec_time
return None

Expand Down Expand Up @@ -765,7 +765,7 @@ def preexec_fn(self):
stderr_fd = subprocess.PIPE

# Note down execution time
self.popen_time = monotonic_time()
self.popen_time = time.monotonic()

# Actually call the Popen
self.popen = self._popen(command,
Expand Down Expand Up @@ -995,8 +995,8 @@ def allow_writing_only(self, inner_paths):
os.path.realpath(os.path.join(self._home_dest, inner_path))
# If an inner path is absolute (e.g., /fifo0/u0_to_m) then
# it may be outside home and we should ignore it.
# FIXME: In Py3 use os.path.commonpath.
if not abs_inner_path.startswith(self._home_dest + "/"):
if os.path.commonpath([abs_inner_path,
self._home_dest]) != self._home_dest:
continue
rel_inner_path = os.path.relpath(abs_inner_path, self._home_dest)
outer_path = os.path.join(self._home, rel_inner_path)
Expand Down
5 changes: 2 additions & 3 deletions cms/io/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
get_service_address
from cms.log import root_logger, shell_handler, ServiceFilter, \
DetailedFormatter, LogServiceHandler, FileHandler
from cmscommon.datetime import monotonic_time
from .rpc import rpc_method, RemoteServiceServer, RemoteServiceClient, \
FakeRemoteServiceClient

Expand All @@ -64,14 +63,14 @@ def repeater(func, period):
"""
while True:
call = monotonic_time()
call = time.monotonic()

try:
func()
except Exception:
logger.error("Unexpected error.", exc_info=True)

gevent.sleep(max(call + period - monotonic_time(), 0))
gevent.sleep(max(call + period - time.monotonic(), 0))


class Service:
Expand Down
5 changes: 2 additions & 3 deletions cms/io/triggeredservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
from gevent.event import Event

from cms.io import PriorityQueue, Service, rpc_method
from cmscommon.datetime import monotonic_time


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -307,7 +306,7 @@ def _sweeper_loop(self):
"""
while True:
self._sweeper_start = monotonic_time()
self._sweeper_start = time.monotonic()
self._sweeper_event.clear()

try:
Expand All @@ -318,7 +317,7 @@ def _sweeper_loop(self):

self._sweeper_event.wait(max(self._sweeper_start +
self._sweeper_timeout -
monotonic_time(), 0))
time.monotonic(), 0))

def _sweep(self):
"""Check for missed operations."""
Expand Down
3 changes: 1 addition & 2 deletions cms/locale/locale.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,8 @@ def format_size(self, n):
n /= self.PREFIX_FACTOR
if n < self.PREFIX_FACTOR:
f = copy.copy(self.locale.decimal_formats[None])
# We need int because floor returns a float in py2.
# if 1000 <= n < 1024 d can be negative, cap to 0 decimals
d = max(0, int(2 - math.floor(math.log10(n))))
d = max(0, 2 - math.floor(math.log10(n)))
f.frac_prec = (d, d)
return (self.gettext(unit)
% babel.numbers.format_decimal(n, format=f,
Expand Down
4 changes: 1 addition & 3 deletions cms/server/contest/handlers/contest.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,7 @@ def get_current_user(self):
cookie = self.get_secure_cookie(cookie_name)

try:
# In py2 Tornado gives us the IP address as a native binary
# string, whereas ipaddress wants text (unicode) strings.
ip_address = ipaddress.ip_address(str(self.request.remote_ip))
ip_address = ipaddress.ip_address(self.request.remote_ip)
except ValueError:
logger.warning("Invalid IP address provided by Tornado: %s",
self.request.remote_ip)
Expand Down
4 changes: 1 addition & 3 deletions cms/server/contest/handlers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,7 @@ def post(self):
password = self.get_argument("password", "")

try:
# In py2 Tornado gives us the IP address as a native binary
# string, whereas ipaddress wants text (unicode) strings.
ip_address = ipaddress.ip_address(str(self.request.remote_ip))
ip_address = ipaddress.ip_address(self.request.remote_ip)
except ValueError:
logger.warning("Invalid IP address provided by Tornado: %s",
self.request.remote_ip)
Expand Down
3 changes: 1 addition & 2 deletions cms/server/contest/tokening.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ def generate_tokens(begin_timestamp, end_timestamp):
if gen_number > 0 and (gen_max is None or avail < gen_max):
num_periods_so_far = \
(timestamp - start).total_seconds() // gen_interval.total_seconds()
# py2 needs the cast to int.
next_gen_time = start + gen_interval * (int(num_periods_so_far) + 1)
next_gen_time = start + gen_interval * (num_periods_so_far + 1)

# If we have more tokens than how many we are allowed to play, cap
# the result, and note that no more will be generated.
Expand Down
15 changes: 3 additions & 12 deletions cms/server/jinja2_toolbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,8 @@ def any_(ctx, l, test=None, *args):
return False


# FIXME once we drop py2 do dictselect(ctx, d, test=None, *args, by="key")
@contextfilter
def dictselect(ctx, d, test=None, *args, **kwargs):
def dictselect(ctx, d, test=None, *args, by="key"):
"""Filter the given dict: keep only items that pass the given test.
ctx (Context): a Jinja2 context, needed to retrieve the test
Expand All @@ -110,10 +109,6 @@ def dictselect(ctx, d, test=None, *args, **kwargs):
test = bool
else:
test = ctx.environment.tests[test]
by = kwargs.pop("by", "key")
if len(kwargs) > 0:
raise ValueError("Invalid keyword argument: %s"
% next(iter(kwargs.keys())))
if by not in {"key", "value"}:
raise ValueError("Invalid value of \"by\" keyword argument: %s" % by)
return dict((k, v) for k, v in d.items()
Expand Down Expand Up @@ -168,10 +163,8 @@ def instrument_generic_toolbox(env):
env.tests["today"] = today


# TODO When dropping py2, let the arguments be `env, *, dataset` in
# order to force the users to pass the dataset as a keyword argument.
@environmentfunction
def safe_get_task_type(env, dataset):
def safe_get_task_type(env, *, dataset):
try:
return dataset.task_type_object
# The task type's constructor is called, which may raise any
Expand All @@ -180,10 +173,8 @@ def safe_get_task_type(env, dataset):
return env.undefined("TaskType not found: %s" % err)


# TODO When dropping py2, let the arguments be `env, *, dataset` in
# order to force the users to pass the dataset as a keyword argument.
@environmentfunction
def safe_get_score_type(env, dataset):
def safe_get_score_type(env, *, dataset):
try:
return dataset.score_type_object
# The score type's constructor is called, which may raise any
Expand Down
9 changes: 4 additions & 5 deletions cms/service/flushingdict.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import logging
import time

import gevent
from gevent.lock import RLock

from cmscommon.datetime import monotonic_time


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -66,13 +65,13 @@ def __init__(self, size, flush_latency_seconds, callback):
self.d_lock = RLock()

# Time when an item was last inserted in the dict
self.last_insert = monotonic_time()
self.last_insert = time.monotonic()

def add(self, key, value):
logger.debug("Adding item %s", key)
with self.d_lock:
self.d[key] = value
self.last_insert = monotonic_time()
self.last_insert = time.monotonic()

def flush(self):
logger.debug("Flushing items")
Expand All @@ -90,7 +89,7 @@ def _check_flush(self):
while True:
while True:
with self.d_lock:
since_last_insert = monotonic_time() - self.last_insert
since_last_insert = time.monotonic() - self.last_insert
if len(self.d) != 0 and (
len(self.d) >= self.size or
since_last_insert > self.flush_latency_seconds):
Expand Down
3 changes: 1 addition & 2 deletions cmscommon/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ def hex_to_bin(hex):


def bin_to_b64(bin):
# TODO: use newline=False instead of strip() when we get rid of Python 2.
return binascii.b2a_base64(bin).strip().decode('ascii')
return binascii.b2a_base64(bin, newline=False).decode('ascii')


def b64_to_bin(b64):
Expand Down
6 changes: 1 addition & 5 deletions cmscommon/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

try:
from shlex import quote
except ImportError:
# Python 2
from pipes import quote
from shlex import quote


__all__ = [
Expand Down
58 changes: 0 additions & 58 deletions cmscommon/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
"get_timezone", "get_system_timezone",

"utc", "local_tz",

"monotonic_time",
]


Expand Down Expand Up @@ -105,59 +103,3 @@ def get_system_timezone():
if hasattr(local_tz, 'zone'):
return local_tz.zone
return local_tz.tzname(make_datetime())


if sys.version_info >= (3, 3):
def monotonic_time():
"""Get the number of seconds passed since a fixed past moment.
A monotonic clock measures the time elapsed since an arbitrary
but immutable instant in the past. The value itself has no
direct intrinsic meaning but the difference between two such
values does, as it is guaranteed to accurately represent the
amount of time passed between when those two measurements were
taken, no matter the adjustments to the clock that occurred in
between.
return (float): the value of the clock, in seconds.
"""
return time.monotonic()

# Taken from http://bugs.python.org/file19461/monotonic.py and
# http://stackoverflow.com/questions/1205722/how-do-i-get-monotonic-time-durations-in-python
# and modified.
else:
from ctypes import Structure, c_long, CDLL, c_int, get_errno, POINTER, \
pointer
from ctypes.util import find_library

# Raw means it's immune even to NTP time adjustments.
CLOCK_MONOTONIC_RAW = 4

class timespec(Structure):
_fields_ = [
('tv_sec', c_long),
('tv_nsec', c_long)
]

librt_filename = find_library('rt')
if not librt_filename:
# On Debian Lenny (Python 2.5.2), find_library() is unable
# to locate /lib/librt.so.1
librt_filename = 'librt.so.1'
librt = CDLL(librt_filename, use_errno=True)
_clock_gettime = librt.clock_gettime
_clock_gettime.argtypes = (c_int, POINTER(timespec))

def monotonic_time():
"""Get the number of seconds passed since a fixed past moment.
return (float): the value of a monotonic clock, in seconds.
"""
t = timespec()
if _clock_gettime(CLOCK_MONOTONIC_RAW, pointer(t)) != 0:
errno_ = get_errno()
raise OSError(errno_, os.strerror(errno_))
return t.tv_sec + t.tv_nsec / 1e9
3 changes: 0 additions & 3 deletions cmscommon/importers.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ def import_testcases_from_zipfile(
tests = dict()
# Match input/output file names to testcases' codenames.
for filename in archive_zfp.namelist():
# In py2, filename is either str (if ASCII) or unicode.
# Cast it to a consistent type, compatible with py3.
filename = str(filename)
match = input_re.match(filename)
if match:
codename = match.group(1)
Expand Down
2 changes: 1 addition & 1 deletion cmscommon/terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def has_color_support(stream):
# See `man terminfo` for capabilities' names and meanings.
if curses.tigetnum("colors") > 0:
return True
# fileno() can raise OSError (since Python 3.3).
# fileno() can raise OSError.
except Exception:
pass
return False
Expand Down
2 changes: 1 addition & 1 deletion cmsranking/Logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def has_color_support(stream):
# See `man terminfo` for capabilities' names and meanings.
if curses.tigetnum("colors") > 0:
return True
# fileno() can raise OSError (since Python 3.3).
# fileno() can raise OSError.
except Exception:
pass
return False
Expand Down
Loading

0 comments on commit 75e4041

Please sign in to comment.