Skip to content

Commit 42632fb

Browse files
authored
Add wrappers around PyFrame and PyCode (#225)
1 parent a72e3ee commit 42632fb

File tree

7 files changed

+174
-0
lines changed

7 files changed

+174
-0
lines changed

example/code.pyi

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from __future__ import annotations
2+
3+
def file_name(): ...
4+
def first_line_number(): ...
5+
def function_name(): ...
6+
def line_number(): ...

example/code.zig

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
const std = @import("std");
14+
const py = @import("pydust");
15+
16+
pub fn line_number() u32 {
17+
return py.PyFrame.get().?.lineNumber();
18+
}
19+
20+
pub fn function_name() !py.PyString {
21+
return py.PyFrame.get().?.code().name();
22+
}
23+
24+
pub fn file_name() !py.PyString {
25+
return py.PyFrame.get().?.code().fileName();
26+
}
27+
28+
pub fn first_line_number() !u32 {
29+
return py.PyFrame.get().?.code().firstLineNumber();
30+
}
31+
32+
comptime {
33+
py.rootmodule(@This());
34+
}

pydust/src/types.zig

+2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
pub usingnamespace @import("types/bool.zig");
1414
pub usingnamespace @import("types/buffer.zig");
1515
pub usingnamespace @import("types/bytes.zig");
16+
pub usingnamespace @import("types/code.zig");
1617
pub usingnamespace @import("types/dict.zig");
1718
pub usingnamespace @import("types/error.zig");
1819
pub usingnamespace @import("types/float.zig");
20+
pub usingnamespace @import("types/frame.zig");
1921
pub usingnamespace @import("types/gil.zig");
2022
pub usingnamespace @import("types/iter.zig");
2123
pub usingnamespace @import("types/list.zig");

pydust/src/types/code.zig

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
const std = @import("std");
14+
const py = @import("../pydust.zig");
15+
const PyObjectMixin = @import("./obj.zig").PyObjectMixin;
16+
17+
const ffi = py.ffi;
18+
19+
/// Wrapper for Python PyCode.
20+
/// See: https://docs.python.org/3/c-api/code.html
21+
pub const PyCode = extern struct {
22+
obj: py.PyObject,
23+
24+
pub inline fn firstLineNumber(self: *const PyCode) !u32 {
25+
const lineNo = try self.obj.getAs(py.PyLong, "co_firstlineno");
26+
defer lineNo.decref();
27+
return lineNo.as(u32);
28+
}
29+
30+
pub inline fn fileName(self: *const PyCode) !py.PyString {
31+
return self.obj.getAs(py.PyString, "co_filename");
32+
}
33+
34+
pub inline fn name(self: *const PyCode) !py.PyString {
35+
return self.obj.getAs(py.PyString, "co_name");
36+
}
37+
};
38+
39+
test "PyCode" {
40+
py.initialize();
41+
defer py.finalize();
42+
43+
const pf = py.PyFrame.get();
44+
try std.testing.expectEqual(@as(?py.PyFrame, null), pf);
45+
}

pydust/src/types/frame.zig

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
const std = @import("std");
14+
const py = @import("../pydust.zig");
15+
const PyObjectMixin = @import("./obj.zig").PyObjectMixin;
16+
17+
const ffi = py.ffi;
18+
19+
/// Wrapper for Python PyFrame.
20+
/// See: https://docs.python.org/3/c-api/frame.html
21+
pub const PyFrame = extern struct {
22+
obj: py.PyObject,
23+
24+
pub fn get() ?PyFrame {
25+
const frame = ffi.PyEval_GetFrame();
26+
return if (frame) |f| .{ .obj = .{ .py = objPtr(f) } } else null;
27+
}
28+
29+
pub fn code(self: PyFrame) py.PyCode {
30+
const codeObj = ffi.PyFrame_GetCode(framePtr(self.obj.py));
31+
return .{ .obj = .{ .py = @alignCast(@ptrCast(codeObj)) } };
32+
}
33+
34+
pub inline fn lineNumber(self: PyFrame) u32 {
35+
return @intCast(ffi.PyFrame_GetLineNumber(framePtr(self.obj.py)));
36+
}
37+
38+
inline fn framePtr(obj: *ffi.PyObject) *ffi.PyFrameObject {
39+
return @alignCast(@ptrCast(obj));
40+
}
41+
42+
inline fn objPtr(obj: *ffi.PyFrameObject) *ffi.PyObject {
43+
return @alignCast(@ptrCast(obj));
44+
}
45+
};
46+
47+
test "PyFrame" {
48+
py.initialize();
49+
defer py.finalize();
50+
51+
const pf = PyFrame.get();
52+
try std.testing.expectEqual(@as(?PyFrame, null), pf);
53+
}

pyproject.toml

+4
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,7 @@ root = "example/iterators.zig"
103103
[[tool.pydust.ext_module]]
104104
name = "example.operators"
105105
root = "example/operators.zig"
106+
107+
[[tool.pydust.ext_module]]
108+
name = "example.code"
109+
root = "example/code.zig"

test/test_code.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
Licensed under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License.
4+
You may obtain a copy of the License at
5+
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
8+
Unless required by applicable law or agreed to in writing, software
9+
distributed under the License is distributed on an "AS IS" BASIS,
10+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the License for the specific language governing permissions and
12+
limitations under the License.
13+
"""
14+
15+
from pathlib import Path
16+
17+
from example import code
18+
19+
20+
def test_line_no():
21+
assert code.line_number() == 21
22+
assert code.first_line_number() == 20
23+
24+
25+
def test_function_name():
26+
assert code.function_name() == "test_function_name"
27+
28+
29+
def test_file_name():
30+
assert Path(code.file_name()).name == "test_code.py"

0 commit comments

Comments
 (0)