Skip to content

Commit 4cf245a

Browse files
author
Hood Chatham
authored
Add cffi package (pyodide#1761)
1 parent e938d58 commit 4cf245a

File tree

16 files changed

+160
-16
lines changed

16 files changed

+160
-16
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ emsdk/emsdk
2525
geckodriver.log
2626
node_modules
2727
packages/.artifacts
28-
packages/*/build.log
28+
packages/*/build.log*
2929
packages/build-logs
3030
dist/

conftest.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ def javascript_setup(self):
133133
pyodide_checks=False,
134134
)
135135

136+
@property
137+
def pyodide_loaded(self):
138+
return self.run_js("return !!(self.pyodide && self.pyodide.runPython);")
139+
136140
@property
137141
def logs(self):
138142
logs = self.run_js("return self.logs;", pyodide_checks=False)
@@ -401,11 +405,11 @@ def pytest_runtest_call(item):
401405
https://github.com/pytest-dev/pytest/issues/5044
402406
"""
403407
selenium = None
404-
if "selenium" in item._fixtureinfo.argnames:
405-
selenium = item.funcargs["selenium"]
406-
if "selenium_standalone" in item._fixtureinfo.argnames:
407-
selenium = item.funcargs["selenium_standalone"]
408-
if selenium:
408+
for fixture in item._fixtureinfo.argnames:
409+
if fixture.startswith("selenium"):
410+
selenium = item.funcargs[fixture]
411+
break
412+
if selenium and selenium.pyodide_loaded:
409413
trace_pyproxies = pytest.mark.skip_pyproxy_check.mark not in item.own_markers
410414
trace_hiwire_refs = (
411415
trace_pyproxies

packages/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export PYODIDE_ROOT=$(abspath ..)
22
PYODIDE_LIBRARIES=$(abspath ./.artifacts)
3+
CPYTHONROOT=$(PYODIDE_ROOT)/cpython
34
include ../Makefile.envs
45

56
export NUMPY_LIB=$(PYODIDE_ROOT)/packages/numpy/build/numpy-1.17.5/install/lib/python$(PYMAJOR).$(PYMINOR)/site-packages/numpy/core/lib/

packages/cffi/meta.yaml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package:
2+
name: cffi
3+
version: 1.14.6
4+
requirements:
5+
run:
6+
- pycparser
7+
source:
8+
url: https://files.pythonhosted.org/packages/2e/92/87bb61538d7e60da8a7ec247dc048f7671afe17016cd0008b3b710012804/cffi-1.14.6.tar.gz
9+
sha256: c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd
10+
patches:
11+
- patches/libffi-config.patch
12+
test:
13+
imports:
14+
- cffi
15+
about:
16+
home: http://cffi.readthedocs.org
17+
PyPi: https://pypi.org/project/cffi
18+
summary: Foreign Function Interface for Python calling C code.
19+
license: MIT
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# HG changeset patch
2+
# User hoodmane
3+
# Date 1627635284 -7200
4+
# Fri Jul 30 10:54:44 2021 +0200
5+
# Branch patch
6+
# Node ID 8d1dd7efc05383a1ff7430ec3c64154a0ecd4be1
7+
# Parent e0e3f0fc068ab19d2a8f6daf1f89dea6a7912b8e
8+
Change config settings
9+
10+
diff -r e0e3f0fc068a -r 8d1dd7efc053 c/_cffi_backend.c
11+
--- a/c/_cffi_backend.c Thu Jul 22 17:44:04 2021 +0200
12+
+++ b/c/_cffi_backend.c Fri Jul 30 10:54:44 2021 +0200
13+
@@ -107,12 +107,12 @@
14+
15+
#else
16+
17+
-# define CFFI_CHECK_FFI_CLOSURE_ALLOC 0
18+
-# define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 0
19+
-# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC 0
20+
-# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 0
21+
-# define CFFI_CHECK_FFI_PREP_CIF_VAR 0
22+
-# define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 0
23+
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC 1
24+
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 1
25+
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC 1
26+
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 1
27+
+# define CFFI_CHECK_FFI_PREP_CIF_VAR 1
28+
+# define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 1
29+
30+
#endif
31+

packages/cffi/test_cffi.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from pyodide_build.testing import run_in_pyodide
2+
3+
4+
@run_in_pyodide(packages=["cffi"])
5+
def test_blah():
6+
from cffi import FFI
7+
8+
ffi = FFI()
9+
ffi.cdef(
10+
"""int asprintf(char** buf, const char *format, ...); // copy-pasted from the man page"""
11+
)
12+
C = ffi.dlopen(None) # loads the entire C namespace
13+
buf = ffi.new("char**")
14+
arg1 = ffi.new("char[]", b"wo")
15+
arg2 = ffi.new("char[]", b"ld")
16+
C.asprintf(buf, b"hello %sr%s", arg1, arg2)
17+
assert ffi.string(buf[0]).decode() == "hello world"

packages/cffi_example/meta.yaml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package:
2+
name: cffi_example
3+
version: "0.1"
4+
requirements:
5+
run:
6+
- cffi
7+
source:
8+
url: https://github.com/wolever/python-cffi-example/archive/ec14da61ee0de770750ff3781b412a3b25f020eb.zip
9+
sha256: 4d7844ff45e5e870dc7996b4cc61636eabd33c32cc8663cf2cd21e3f4f893abc
10+
test:
11+
imports:
12+
- cffi_example
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import pytest
2+
from pyodide_build.testing import run_in_pyodide
3+
4+
5+
@pytest.mark.parametrize(
6+
"pattern,name,flags,expected",
7+
[
8+
("foo", "bar", 0, False),
9+
("f*", "foo", 0, True),
10+
("f*bar", "f/bar", 0, True),
11+
("f*bar", "f/bar", "fnmatch.FNM_PATHNAME", False),
12+
],
13+
)
14+
def test_fnmatch(selenium_module_scope, pattern, name, flags, expected):
15+
selenium = selenium_module_scope
16+
selenium.load_package("cffi_example")
17+
result = selenium.run(
18+
f"""
19+
from cffi_example import fnmatch
20+
fnmatch.fnmatch({pattern!r}, {name!r}, {flags})
21+
"""
22+
)
23+
assert result == expected
24+
25+
26+
@run_in_pyodide(packages=["cffi_example"], module_scope=True)
27+
def test_person():
28+
from cffi_example.person import Person
29+
30+
p = Person("Alex", "Smith", 72)
31+
assert p.get_age() == 72
32+
assert p.get_full_name() == "Alex Smith"
33+
34+
p = Person("x" * 100, "y" * 100, 72)
35+
assert p.get_full_name() == "x" * 100

packages/libyaml/meta.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package:
44

55
source:
66
url: https://github.com/yaml/libyaml/archive/0.2.1.zip
7+
sha256: 56070c9d4bf244a8dcc68e04613e5bbce5c8411ed97cdccc1f4b5fb46aebe5a8
78

89
build:
910
library: true

packages/micropip/test_micropip.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@ def test_install_simple(selenium_standalone_micropip):
2626
assert (
2727
selenium.run_js(
2828
"""
29-
let result = await pyodide.runPythonAsync(`
29+
return await pyodide.runPythonAsync(`
3030
import os
3131
import micropip
32+
from pyodide import to_js
3233
# Package 'pyodide-micropip-test' has dependency on 'snowballstemmer'
3334
# It is used to test markers support
3435
await micropip.install('pyodide-micropip-test')
3536
import snowballstemmer
3637
stemmer = snowballstemmer.stemmer('english')
37-
stemmer.stemWords('go going goes gone'.split())
38+
to_js(stemmer.stemWords('go going goes gone'.split()))
3839
`);
39-
return result.toJs();
4040
"""
4141
)
4242
== ["go", "go", "goe", "gone"]

packages/pycparser/meta.yaml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package:
2+
name: pycparser
3+
version: "2.20"
4+
source:
5+
url: https://files.pythonhosted.org/packages/0f/86/e19659527668d70be91d0369aeaa055b4eb396b0f387a4f92293a20035bd/pycparser-2.20.tar.gz
6+
sha256: 2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0
7+
test:
8+
imports:
9+
- pycparser
10+
about:
11+
home: https://github.com/eliben/pycparser
12+
PyPi: https://pypi.org/project/pycparser
13+
summary: C parser in Python
14+
license: BSD

packages/pyyaml/meta.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package:
44

55
source:
66
url: https://github.com/yaml/pyyaml/archive/refs/tags/5.4.1.tar.gz
7+
sha256: 75f966559c5f262dfc44da0f958cc2aa18953ae5021f2c3657b415c5a370045f
78

89
patches:
910
- patches/Makefile.patch

pyodide-build/pyodide_build/buildpkg.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def download_and_extract(
9999
srcdir = Path(pkg["source"]["path"])
100100

101101
if not srcdir.is_dir():
102-
raise ValueError("'path' must point to a path")
102+
raise ValueError(f"path={srcdir} must point to a directory that exists")
103103

104104
if not srcpath.is_dir():
105105
shutil.copytree(srcdir, srcpath)

pyodide-build/pyodide_build/pywasmcross.py

+2
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ def handle_command(line, args, dryrun=False):
327327
# On Mac, we need to omit some darwin-specific arguments
328328
if arg in ["-bundle", "-undefined", "dynamic_lookup"]:
329329
continue
330+
if arg == "-lffi":
331+
continue
330332
# The native build is possibly multithreaded, but the emscripten one
331333
# definitely isn't
332334
arg = re.sub(r"/python([0-9]\.[0-9]+)m", r"/python\1", arg)

pyodide-build/pyodide_build/testing.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def run_in_pyodide(
2929
_function: Optional[Callable] = None,
3030
*,
3131
standalone: bool = False,
32+
module_scope: bool = False,
3233
packages: List[str] = [],
3334
xfail_browsers: Dict[str, str] = {},
3435
driver_timeout: Optional[Union[str, int]] = None,
@@ -107,17 +108,20 @@ def inner(selenium):
107108

108109
if standalone:
109110

110-
def wrapped_standalone(selenium_standalone):
111+
def wrapped(selenium_standalone): # type: ignore
111112
inner(selenium_standalone)
112113

113-
return wrapped_standalone
114+
elif module_scope:
115+
116+
def wrapped(selenium_module_scope): # type: ignore
117+
inner(selenium_module_scope)
114118

115119
else:
116120

117-
def wrapped(selenium):
121+
def wrapped(selenium): # type: ignore
118122
inner(selenium)
119123

120-
return wrapped
124+
return wrapped
121125

122126
if _function is not None:
123127
return decorator(_function)

src/tests/test_typeconversions.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
def test_string_conversion(selenium_module_scope, s):
1212
with selenium_context_manager(selenium_module_scope) as selenium:
1313
# careful string escaping here -- hypothesis will fuzz it.
14+
# Note: using base 64 encoding would be much more compact...
1415
sbytes = list(s.encode())
1516
selenium.run_js(
1617
f"""
@@ -111,6 +112,8 @@ def test_bigint_conversions(selenium_module_scope, n):
111112

112113

113114
# Generate an object of any type
115+
@pytest.mark.skip_refcount_check
116+
@pytest.mark.skip_pyproxy_check
114117
@given(obj=from_type(type).flatmap(from_type))
115118
@settings(deadline=2000)
116119
def test_hyp_py2js2py(selenium_module_scope, obj):
@@ -178,6 +181,8 @@ def test_big_integer_py2js2py(selenium):
178181

179182

180183
# Generate an object of any type
184+
@pytest.mark.skip_refcount_check
185+
@pytest.mark.skip_pyproxy_check
181186
@given(obj=from_type(type).flatmap(from_type))
182187
@settings(deadline=2000)
183188
def test_hyp_tojs_no_crash(selenium_module_scope, obj):
@@ -270,8 +275,6 @@ def test_python2js5(selenium):
270275
)
271276

272277

273-
# @pytest.mark.skip_refcount_check
274-
# @pytest.mark.skip_pyproxy_check
275278
def test_python2js_track_proxies(selenium):
276279
selenium.run_js(
277280
"""

0 commit comments

Comments
 (0)