Skip to content

Commit a090616

Browse files
author
Christopher Doris
committed
stop using Requires.jl (use extensions instead)
1 parent 0b32b08 commit a090616

File tree

9 files changed

+74
-60
lines changed

9 files changed

+74
-60
lines changed

Project.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,41 @@ Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
1010
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
1111
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
1212
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
13-
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
1413
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
1514
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
1615
UnsafePointers = "e17b2a0c-0bdf-430a-bd0c-3a23cae4ff39"
1716

1817
[compat]
1918
Aqua = "0 - 999"
19+
CategoricalArrays = "0.10, 1"
2020
CondaPkg = "0.2.30"
2121
Dates = "1"
2222
Libdl = "1"
2323
MacroTools = "0.5"
2424
Markdown = "1"
2525
Pkg = "1"
2626
PyCall = "1"
27-
Requires = "1"
2827
Serialization = "1"
2928
Tables = "1"
3029
Test = "1"
3130
TestItemRunner = "0 - 999"
3231
UnsafePointers = "1"
3332
julia = "1.9"
3433

34+
[extensions]
35+
PyCallExt = "PyCall"
36+
CategoricalArraysExt = "CategoricalArrays"
37+
3538
[extras]
3639
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
40+
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
3741
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
3842
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3943
TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a"
4044

4145
[targets]
4246
test = ["Aqua", "PyCall", "Test", "TestItemRunner"]
47+
48+
[weakdeps]
49+
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
50+
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"

ext/CategoricalArraysExt.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module CategoricalArraysExt
2+
3+
using PythonCall
4+
5+
using CategoricalArrays: CategoricalArrays
6+
7+
function PythonCall.Compat.aspandasvector(x::CategoricalArrays.CategoricalArray)
8+
codes = map(x -> x === missing ? -1 : Int(CategoricalArrays.levelcode(x)) - 1, x)
9+
cats = CategoricalArrays.levels(x)
10+
ordered = x.pool.ordered
11+
pyimport("pandas").Categorical.from_codes(codes, cats, ordered = ordered)
12+
end
13+
14+
end

ext/PyCallExt.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
module PyCallExt
2+
3+
using PythonCall
4+
using PythonCall.Core
5+
using PythonCall.C
6+
7+
using PyCall: PyCall
8+
9+
import PythonCall: Py
10+
11+
# true if PyCall and PythonCall are using the same interpreter
12+
const SAME = Ref{Bool}(false)
13+
14+
function __init__()
15+
# see if PyCall and PythonCall are using the same interpreter by checking if a couple of memory addresses are the same
16+
ptr1 = C.Py_GetVersion()
17+
ptr2 = ccall(PyCall.@pysym(:Py_GetVersion), Ptr{Cchar}, ())
18+
SAME[] = ptr1 == ptr2
19+
if PythonCall.C.CTX.which == :PyCall
20+
@assert SAME[]
21+
end
22+
end
23+
24+
# allow explicit conversion between PythonCall.Py and PyCall.PyObject
25+
# provided they are using the same interpretr
26+
const ERRMSG = """
27+
Conversion between `PyCall.PyObject` and `PythonCall.Py` is only possible when using the same Python interpreter.
28+
29+
There are two ways to achieve this:
30+
- Set the environment variable `JULIA_PYTHONCALL_EXE` to `"@PyCall"`. This forces PythonCall to use the same
31+
interpreter as PyCall, but PythonCall loses the ability to manage its own dependencies.
32+
- Set the environment variable `PYTHON` to `PythonCall.python_executable_path()` and rebuild PyCall. This forces
33+
PyCall to use the same interpreter as PythonCall, but needs to be repeated whenever you switch Julia environment.
34+
"""
35+
36+
function Py(x::PyCall.PyObject)
37+
SAME[] || error(ERRMSG)
38+
return pynew(C.PyPtr(PyCall.pyreturn(x)))
39+
end
40+
41+
function PyCall.PyObject(x::Py)
42+
SAME[] || error(ERRMSG)
43+
return PyCall.PyObject(PyCall.PyPtr(getptr(incref(x))))
44+
end
45+
46+
end

src/C/C.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ using Base: @kwdef
99
using UnsafePointers: UnsafePtr
1010
using CondaPkg: CondaPkg
1111
using Pkg: Pkg
12-
using Requires: @require
1312
using Libdl:
1413
dlpath, dlopen, dlopen_e, dlclose, dlsym, dlsym_e, RTLD_LAZY, RTLD_DEEPBIND, RTLD_GLOBAL
1514

16-
import ..PythonCall: python_executable_path, python_library_path, python_library_handle, python_version
15+
import ..PythonCall:
16+
python_executable_path, python_library_path, python_library_handle, python_version
1717

1818

1919
include("consts.jl")

src/C/context.jl

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ A handle to a loaded instance of libpython, its interpreter, function pointers,
1717
pyhome_w::Any = missing
1818
which::Symbol = :unknown # :CondaPkg, :PyCall, :embedded or :unknown
1919
version::Union{VersionNumber,Missing} = missing
20-
matches_pycall::Union{Bool,Missing} = missing
2120
end
2221

2322
const CTX = Context()
@@ -141,15 +140,9 @@ function init_context()
141140
# Get function pointers from the library
142141
init_pointers()
143142

144-
# Compare libpath with PyCall
145-
@require PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" init_pycall(PyCall)
146-
147143
# Initialize the interpreter
148144
CTX.is_preinitialized = Py_IsInitialized() != 0
149-
if CTX.is_preinitialized
150-
@assert CTX.which == :PyCall || CTX.matches_pycall isa Bool
151-
else
152-
@assert CTX.which != :PyCall
145+
if !CTX.is_preinitialized
153146
# Find ProgramName and PythonHome
154147
script = if Sys.iswindows()
155148
"""
@@ -243,13 +236,3 @@ const PYTHONCALL_PKGID = Base.PkgId(PYTHONCALL_UUID, "PythonCall")
243236

244237
const PYCALL_UUID = Base.UUID("438e738f-606a-5dbb-bf0a-cddfbfd45ab0")
245238
const PYCALL_PKGID = Base.PkgId(PYCALL_UUID, "PyCall")
246-
247-
function init_pycall(PyCall::Module)
248-
# see if PyCall and PythonCall are using the same interpreter by checking if a couple of memory addresses are the same
249-
ptr1 = Py_GetVersion()
250-
ptr2 = @eval PyCall ccall(@pysym(:Py_GetVersion), Ptr{Cchar}, ())
251-
CTX.matches_pycall = ptr1 == ptr2
252-
if CTX.which == :PyCall
253-
@assert CTX.matches_pycall
254-
end
255-
end

src/Compat/Compat.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ using ..Wrap
1313

1414
using Serialization: Serialization, AbstractSerializer, serialize, deserialize
1515
using Tables: Tables
16-
using Requires: @require
1716

1817
import ..PythonCall: event_loop_on, event_loop_off, fix_qt_plugin_path, pytable
1918

@@ -22,13 +21,10 @@ include("ipython.jl")
2221
include("multimedia.jl")
2322
include("serialization.jl")
2423
include("tables.jl")
25-
include("pycall.jl")
2624

2725
function __init__()
2826
init_gui()
2927
init_pyshow()
30-
init_tables()
31-
@require PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" init_pycall(PyCall)
3228
end
3329

3430
end

src/Compat/pycall.jl

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/Compat/tables.jl

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,3 @@ function _pytable_pandas(src, cols = Tables.columns(src); opts...)
6363
opts...,
6464
)
6565
end
66-
67-
function init_tables()
68-
@require CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" @eval begin
69-
aspandasvector(x::CategoricalArrays.CategoricalArray) = begin
70-
codes = map(x -> x === missing ? -1 : Int(CategoricalArrays.levelcode(x)) - 1, x)
71-
cats = CategoricalArrays.levels(x)
72-
ordered = x.pool.ordered
73-
pyimport("pandas").Categorical.from_codes(codes, cats, ordered = ordered)
74-
end
75-
end
76-
end

test/Compat.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,10 @@ end
3939
end
4040

4141
@testitem "PyCall.jl" setup = [PyCall] begin
42+
@test Base.get_extension(PythonCall, :PyCallExt).SAME[]
4243
x1 = pylist()
4344
x2 = PyCall.PyObject(x1)
4445
x3 = Py(x2)
45-
@test PythonCall.C.CTX.matches_pycall
46-
@test pyisinstance(x3, pybuiltins.list)
4746
@test pyis(x3, x1)
4847
end
4948

0 commit comments

Comments
 (0)