From 6730745da343900506dcb53b56cef5a760b1a66b Mon Sep 17 00:00:00 2001 From: Robert Kruszewski Date: Fri, 27 Oct 2023 16:47:59 +0100 Subject: [PATCH 1/5] Add wrappers around PyFrame and PyCode --- example/code.zig | 34 ++++++++++++++++++++++++ pydust/src/types.zig | 2 ++ pydust/src/types/code.zig | 43 +++++++++++++++++++++++++++++++ pydust/src/types/frame.zig | 53 ++++++++++++++++++++++++++++++++++++++ pyproject.toml | 4 +++ test/test_code.py | 30 +++++++++++++++++++++ 6 files changed, 166 insertions(+) create mode 100644 example/code.zig create mode 100644 pydust/src/types/code.zig create mode 100644 pydust/src/types/frame.zig create mode 100644 test/test_code.py diff --git a/example/code.zig b/example/code.zig new file mode 100644 index 00000000..1ccd5fe6 --- /dev/null +++ b/example/code.zig @@ -0,0 +1,34 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const std = @import("std"); +const py = @import("pydust"); + +pub fn line_number() u64 { + return py.PyFrame.get().?.lineNumber(); +} + +pub fn function_name() !py.PyString { + return py.PyFrame.get().?.code().name(); +} + +pub fn file_name() !py.PyString { + return py.PyFrame.get().?.code().fileName(); +} + +pub fn first_line_number() !u64 { + return py.PyFrame.get().?.code().firstLineNumber(); +} + +comptime { + py.rootmodule(@This()); +} diff --git a/pydust/src/types.zig b/pydust/src/types.zig index c37760b2..8dd5b535 100644 --- a/pydust/src/types.zig +++ b/pydust/src/types.zig @@ -13,9 +13,11 @@ pub usingnamespace @import("types/bool.zig"); pub usingnamespace @import("types/buffer.zig"); pub usingnamespace @import("types/bytes.zig"); +pub usingnamespace @import("types/code.zig"); pub usingnamespace @import("types/dict.zig"); pub usingnamespace @import("types/error.zig"); pub usingnamespace @import("types/float.zig"); +pub usingnamespace @import("types/frame.zig"); pub usingnamespace @import("types/gil.zig"); pub usingnamespace @import("types/iter.zig"); pub usingnamespace @import("types/list.zig"); diff --git a/pydust/src/types/code.zig b/pydust/src/types/code.zig new file mode 100644 index 00000000..611f81f9 --- /dev/null +++ b/pydust/src/types/code.zig @@ -0,0 +1,43 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const std = @import("std"); +const py = @import("../pydust.zig"); +const PyObjectMixin = @import("./obj.zig").PyObjectMixin; + +const ffi = py.ffi; + +/// Wrapper for Python PyCode. +/// See: https://docs.python.org/3/c-api/code.html +pub const PyCode = extern struct { + obj: py.PyObject, + + pub fn firstLineNumber(self: *const PyCode) !u64 { + return self.obj.getAs(u64, "co_firstlineno"); + } + + pub fn fileName(self: *const PyCode) !py.PyString { + return self.obj.getAs(py.PyString, "co_filename"); + } + + pub fn name(self: *const PyCode) !py.PyString { + return self.obj.getAs(py.PyString, "co_name"); + } +}; + +test "PyCode" { + py.initialize(); + defer py.finalize(); + + const pf = py.PyFrame.get(); + try std.testing.expectEqual(@as(?py.PyFrame, null), pf); +} diff --git a/pydust/src/types/frame.zig b/pydust/src/types/frame.zig new file mode 100644 index 00000000..4e30b2fa --- /dev/null +++ b/pydust/src/types/frame.zig @@ -0,0 +1,53 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const std = @import("std"); +const py = @import("../pydust.zig"); +const PyObjectMixin = @import("./obj.zig").PyObjectMixin; + +const ffi = py.ffi; + +/// Wrapper for Python PyFrame. +/// See: https://docs.python.org/3/c-api/frame.html +pub const PyFrame = extern struct { + obj: py.PyObject, + + pub fn get() ?PyFrame { + const frame = ffi.PyEval_GetFrame(); + return if (frame) |f| .{ .obj = .{ .py = objPtr(f) } } else null; + } + + pub fn code(self: PyFrame) py.PyCode { + const codeObj = ffi.PyFrame_GetCode(framePtr(self.obj.py)); + return .{ .obj = .{ .py = @alignCast(@ptrCast(codeObj)) } }; + } + + pub fn lineNumber(self: PyFrame) u32 { + return @intCast(ffi.PyFrame_GetLineNumber(framePtr(self.obj.py))); + } + + fn framePtr(obj: *ffi.PyObject) *ffi.PyFrameObject { + return @alignCast(@ptrCast(obj)); + } + + fn objPtr(obj: *ffi.PyFrameObject) *ffi.PyObject { + return @alignCast(@ptrCast(obj)); + } +}; + +test "PyFrame" { + py.initialize(); + defer py.finalize(); + + const pf = PyFrame.get(); + try std.testing.expectEqual(@as(?PyFrame, null), pf); +} diff --git a/pyproject.toml b/pyproject.toml index 1cb7b62d..34632a6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,3 +103,7 @@ root = "example/iterators.zig" [[tool.pydust.ext_module]] name = "example.operators" root = "example/operators.zig" + +[[tool.pydust.ext_module]] +name = "example.code" +root = "example/code.zig" diff --git a/test/test_code.py b/test/test_code.py new file mode 100644 index 00000000..dbf2382f --- /dev/null +++ b/test/test_code.py @@ -0,0 +1,30 @@ +""" +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from pathlib import Path + +from example import code + + +def test_line_no(): + assert code.line_number() == 21 + assert code.first_line_number() == 20 + + +def test_function_name(): + assert code.function_name() == "test_function_name" + + +def test_file_name(): + assert Path(code.file_name()).name == "test_code.py" From e25539528f447ffaf651f8cc818596aed736c084 Mon Sep 17 00:00:00 2001 From: Robert Kruszewski Date: Fri, 27 Oct 2023 16:51:36 +0100 Subject: [PATCH 2/5] stubs --- example/code.pyi | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 example/code.pyi diff --git a/example/code.pyi b/example/code.pyi new file mode 100644 index 00000000..67f7f12b --- /dev/null +++ b/example/code.pyi @@ -0,0 +1,6 @@ +from __future__ import annotations + +def file_name(): ... +def first_line_number(): ... +def function_name(): ... +def line_number(): ... From ac97e92dc2a20858c3f0ba32a7bdbe8f3b95c44c Mon Sep 17 00:00:00 2001 From: Robert Kruszewski Date: Fri, 27 Oct 2023 20:16:24 +0100 Subject: [PATCH 3/5] better --- pydust/src/types/code.zig | 10 ++++++---- pydust/src/types/frame.zig | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pydust/src/types/code.zig b/pydust/src/types/code.zig index 611f81f9..4ea83796 100644 --- a/pydust/src/types/code.zig +++ b/pydust/src/types/code.zig @@ -21,15 +21,17 @@ const ffi = py.ffi; pub const PyCode = extern struct { obj: py.PyObject, - pub fn firstLineNumber(self: *const PyCode) !u64 { - return self.obj.getAs(u64, "co_firstlineno"); + pub inline fn firstLineNumber(self: *const PyCode) !u32 { + const lineNo = try self.obj.getAs(py.PyLong, "co_firstlineno"); + defer lineNo.decref(); + return lineNo.as(u32); } - pub fn fileName(self: *const PyCode) !py.PyString { + pub inline fn fileName(self: *const PyCode) !py.PyString { return self.obj.getAs(py.PyString, "co_filename"); } - pub fn name(self: *const PyCode) !py.PyString { + pub inline fn name(self: *const PyCode) !py.PyString { return self.obj.getAs(py.PyString, "co_name"); } }; diff --git a/pydust/src/types/frame.zig b/pydust/src/types/frame.zig index 4e30b2fa..8278eb47 100644 --- a/pydust/src/types/frame.zig +++ b/pydust/src/types/frame.zig @@ -31,7 +31,7 @@ pub const PyFrame = extern struct { return .{ .obj = .{ .py = @alignCast(@ptrCast(codeObj)) } }; } - pub fn lineNumber(self: PyFrame) u32 { + pub inline fn lineNumber(self: PyFrame) u32 { return @intCast(ffi.PyFrame_GetLineNumber(framePtr(self.obj.py))); } From 2c1364b987a179a56ac7388cee60e3a46ec4bac9 Mon Sep 17 00:00:00 2001 From: Robert Kruszewski Date: Fri, 27 Oct 2023 20:17:32 +0100 Subject: [PATCH 4/5] better --- pydust/src/types/frame.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pydust/src/types/frame.zig b/pydust/src/types/frame.zig index 8278eb47..452d6950 100644 --- a/pydust/src/types/frame.zig +++ b/pydust/src/types/frame.zig @@ -35,11 +35,11 @@ pub const PyFrame = extern struct { return @intCast(ffi.PyFrame_GetLineNumber(framePtr(self.obj.py))); } - fn framePtr(obj: *ffi.PyObject) *ffi.PyFrameObject { + inline fn framePtr(obj: *ffi.PyObject) *ffi.PyFrameObject { return @alignCast(@ptrCast(obj)); } - fn objPtr(obj: *ffi.PyFrameObject) *ffi.PyObject { + inline fn objPtr(obj: *ffi.PyFrameObject) *ffi.PyObject { return @alignCast(@ptrCast(obj)); } }; From 452ebab16d72927a24d218885db2582bd9c60443 Mon Sep 17 00:00:00 2001 From: Robert Kruszewski Date: Fri, 27 Oct 2023 20:37:01 +0100 Subject: [PATCH 5/5] types --- example/code.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/code.zig b/example/code.zig index 1ccd5fe6..c9224d3a 100644 --- a/example/code.zig +++ b/example/code.zig @@ -13,7 +13,7 @@ const std = @import("std"); const py = @import("pydust"); -pub fn line_number() u64 { +pub fn line_number() u32 { return py.PyFrame.get().?.lineNumber(); } @@ -25,7 +25,7 @@ pub fn file_name() !py.PyString { return py.PyFrame.get().?.code().fileName(); } -pub fn first_line_number() !u64 { +pub fn first_line_number() !u32 { return py.PyFrame.get().?.code().firstLineNumber(); }