Skip to content

Commit

Permalink
Add support for PyPy
Browse files Browse the repository at this point in the history
This skips building the C extension and only uses the pure Python
version when built on PyPy. This is probably the right thing to do on
PyPy anyway, since PyPy needs to use a compatibility layer for the C
extension, and that slows things down considerably.

Running the benchmark suite, the C extension is consistently 10x slower
than the pure Python implementation on PyPy.
  • Loading branch information
pganssle committed May 28, 2020
1 parent e3ca866 commit efc8f57
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 6 deletions.
13 changes: 9 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import os
import platform

import setuptools
from setuptools import Extension

c_extension = Extension(
"backports.zoneinfo._czoneinfo", sources=["lib/zoneinfo_module.c"],
)
if platform.python_implementation() != "PyPy":
c_extension = Extension(
"backports.zoneinfo._czoneinfo", sources=["lib/zoneinfo_module.c"],
)

setuptools.setup(ext_modules=[c_extension])
else:
setuptools.setup()

setuptools.setup(ext_modules=[c_extension])

if "GCNO_TARGET_DIR" in os.environ:
import glob
Expand Down
11 changes: 10 additions & 1 deletion tests/_support.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import contextlib
import functools
import platform
import sys
import threading
import unittest
Expand All @@ -9,6 +10,8 @@
TZPATH_LOCK = threading.Lock()
TZPATH_TEST_LOCK = threading.Lock()

IS_PYPY = platform.python_implementation() == "PyPy"


def call_once(f):
"""Decorator that ensures a function is only ever called once."""
Expand All @@ -33,6 +36,13 @@ def get_modules():
one time — in other words, when using this function you will only ever
get one copy of each module rather than a fresh import each time.
"""
# PyPy doesn't have a C extension, so for the moment we'll just give it
# two copies of the normal module
if IS_PYPY:
from backports import zoneinfo

return zoneinfo, zoneinfo

# The standard import_fresh_module approach seems to be somewhat buggy
# when it comes to C imports, so in the short term, we will do a little
# module surgery to test this.
Expand All @@ -45,7 +55,6 @@ def get_modules():

py_module.ZoneInfo = py_zoneinfo.ZoneInfo
c_module.ZoneInfo = c_zoneinfo.ZoneInfo

return py_module, c_module


Expand Down
4 changes: 3 additions & 1 deletion tests/test_zoneinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from datetime import date, datetime, time, timedelta, timezone

from . import _support as test_support
from ._support import OS_ENV_LOCK, TZPATH_TEST_LOCK, ZoneInfoTestBase
from ._support import IS_PYPY, OS_ENV_LOCK, TZPATH_TEST_LOCK, ZoneInfoTestBase

try:
from functools import cached_property
Expand All @@ -36,6 +36,7 @@
except importlib_metadata.PackageNotFoundError:
HAS_TZDATA_PKG = False


ZONEINFO_DATA = None
ZONEINFO_DATA_V1 = None
TEMP_DIR = None
Expand Down Expand Up @@ -1771,6 +1772,7 @@ class CTestModule(TestModule):
module = c_zoneinfo


@unittest.skipIf(IS_PYPY, "C Extension not built on PyPy")
class ExtensionBuiltTest(unittest.TestCase):
"""Smoke test to ensure that the C and Python extensions are both tested.
Expand Down

0 comments on commit efc8f57

Please sign in to comment.