pycall 1.0.1-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +41 -0
- data/CHANGES.md +39 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +91 -0
- data/Rakefile +29 -0
- data/appveyor.yml +138 -0
- data/bin/console +10 -0
- data/bin/guard +17 -0
- data/bin/rspec +17 -0
- data/bin/runner +6 -0
- data/bin/setup +8 -0
- data/config/Guardfile +30 -0
- data/docker/Dockerfile +191 -0
- data/docker/Gemfile +12 -0
- data/docker/README.md +22 -0
- data/examples/classifier_comparison.rb +135 -0
- data/examples/datascience_rb_20170519.ipynb +4836 -0
- data/examples/hist.rb +32 -0
- data/examples/notebooks/classifier_comparison.ipynb +226 -0
- data/examples/notebooks/forest_importances.ipynb +238 -0
- data/examples/notebooks/iruby_integration.ipynb +183 -0
- data/examples/notebooks/lorenz_attractor.ipynb +214 -0
- data/examples/notebooks/polar_axes.ipynb +209 -0
- data/examples/notebooks/sum_benchmarking.ipynb +374 -0
- data/examples/notebooks/xkcd_style.ipynb +149 -0
- data/examples/plot_forest_importances_faces.rb +46 -0
- data/examples/sum_benchmarking.rb +49 -0
- data/ext/pycall/extconf.rb +3 -0
- data/ext/pycall/gc.c +74 -0
- data/ext/pycall/libpython.c +217 -0
- data/ext/pycall/pycall.c +2184 -0
- data/ext/pycall/pycall_internal.h +700 -0
- data/ext/pycall/range.c +69 -0
- data/ext/pycall/ruby_wrapper.c +432 -0
- data/lib/pycall.rb +91 -0
- data/lib/pycall/conversion.rb +173 -0
- data/lib/pycall/dict.rb +48 -0
- data/lib/pycall/error.rb +10 -0
- data/lib/pycall/gc_guard.rb +84 -0
- data/lib/pycall/import.rb +120 -0
- data/lib/pycall/init.rb +55 -0
- data/lib/pycall/iruby_helper.rb +40 -0
- data/lib/pycall/libpython.rb +12 -0
- data/lib/pycall/libpython/finder.rb +170 -0
- data/lib/pycall/libpython/pyobject_struct.rb +30 -0
- data/lib/pycall/libpython/pytypeobject_struct.rb +273 -0
- data/lib/pycall/list.rb +45 -0
- data/lib/pycall/pretty_print.rb +9 -0
- data/lib/pycall/pyerror.rb +30 -0
- data/lib/pycall/pyobject_wrapper.rb +212 -0
- data/lib/pycall/python/PyCall/__init__.py +1 -0
- data/lib/pycall/python/PyCall/six.py +23 -0
- data/lib/pycall/python/investigator.py +7 -0
- data/lib/pycall/pytypeobject_wrapper.rb +90 -0
- data/lib/pycall/set.rb +19 -0
- data/lib/pycall/slice.rb +8 -0
- data/lib/pycall/tuple.rb +46 -0
- data/lib/pycall/version.rb +3 -0
- data/lib/pycall/wrapper_object_cache.rb +61 -0
- data/pycall.gemspec +40 -0
- data/tasks/docker.rake +21 -0
- data/tasks/pycall.rake +7 -0
- metadata +228 -0
data/lib/pycall/init.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module PyCall
|
2
|
+
def self.const_missing(name)
|
3
|
+
case name
|
4
|
+
when :PyPtr, :PyTypePtr, :PyObjectWrapper, :PYTHON_DESCRIPTION, :PYTHON_VERSION
|
5
|
+
PyCall.init
|
6
|
+
const_get(name)
|
7
|
+
else
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module LibPython
|
13
|
+
def self.const_missing(name)
|
14
|
+
case name
|
15
|
+
when :API, :Conversion, :Helpers, :PYTHON_DESCRIPTION, :PYTHON_VERSION
|
16
|
+
PyCall.init
|
17
|
+
const_get(name)
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.init(python = ENV['PYTHON'])
|
25
|
+
return false if LibPython.instance_variable_defined?(:@handle)
|
26
|
+
class << PyCall
|
27
|
+
remove_method :const_missing
|
28
|
+
end
|
29
|
+
class << PyCall::LibPython
|
30
|
+
remove_method :const_missing
|
31
|
+
end
|
32
|
+
|
33
|
+
ENV['PYTHONPATH'] = [ File.expand_path('../python', __FILE__), ENV['PYTHONPATH'] ].compact.join(File::PATH_SEPARATOR)
|
34
|
+
|
35
|
+
LibPython.instance_variable_set(:@handle, LibPython::Finder.find_libpython(python))
|
36
|
+
class << LibPython
|
37
|
+
undef_method :handle
|
38
|
+
attr_reader :handle
|
39
|
+
end
|
40
|
+
|
41
|
+
begin
|
42
|
+
major, minor, _ = RUBY_VERSION.split('.')
|
43
|
+
require "#{major}.#{minor}/pycall.so"
|
44
|
+
rescue LoadError
|
45
|
+
require 'pycall.so'
|
46
|
+
end
|
47
|
+
|
48
|
+
require 'pycall/dict'
|
49
|
+
require 'pycall/list'
|
50
|
+
require 'pycall/slice'
|
51
|
+
const_set(:PYTHON_VERSION, LibPython::PYTHON_VERSION)
|
52
|
+
const_set(:PYTHON_DESCRIPTION, LibPython::PYTHON_DESCRIPTION)
|
53
|
+
true
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'pycall'
|
2
|
+
require 'iruby'
|
3
|
+
|
4
|
+
module PyCall
|
5
|
+
module IRubyHelper
|
6
|
+
private
|
7
|
+
|
8
|
+
def check_pyobject_respond_to_format_method(obj, method_name)
|
9
|
+
return false unless obj.kind_of? PyObject
|
10
|
+
return false unless PyCall.hasattr?(obj, method_name)
|
11
|
+
PyCall.getattr(obj, method_name).kind_of? PyCall::LibPython.PyMethod_Type
|
12
|
+
end
|
13
|
+
|
14
|
+
def register_pyobject_formatter(format_name, mime, priority_value=0)
|
15
|
+
method_name = :"_repr_#{format_name}_"
|
16
|
+
match do |obj|
|
17
|
+
check_pyobject_respond_to_format_method(obj, method_name)
|
18
|
+
end
|
19
|
+
priority priority_value
|
20
|
+
format mime do |obj|
|
21
|
+
PyCall.getattr(obj, method_name).()
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
::IRuby::Display::Registry.module_eval do
|
28
|
+
extend PyCall::IRubyHelper
|
29
|
+
|
30
|
+
register_pyobject_formatter :html, 'text/html'
|
31
|
+
register_pyobject_formatter :markdown, 'text/markdown'
|
32
|
+
register_pyobject_formatter :svg, 'image/svg+xml'
|
33
|
+
register_pyobject_formatter :png, 'image/png'
|
34
|
+
register_pyobject_formatter :jpeg, 'image/jpeg'
|
35
|
+
register_pyobject_formatter :latex, 'text/latex'
|
36
|
+
register_pyobject_formatter :json, 'application/json'
|
37
|
+
register_pyobject_formatter :javascript, 'application/javascript'
|
38
|
+
register_pyobject_formatter :pdf, 'application/pdf'
|
39
|
+
register_pyobject_formatter :pretty, 'text/plain', -1000
|
40
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'pycall/error'
|
2
|
+
require 'fiddle'
|
3
|
+
|
4
|
+
module PyCall
|
5
|
+
module LibPython
|
6
|
+
module Finder
|
7
|
+
case RUBY_PLATFORM
|
8
|
+
when /cygwin/
|
9
|
+
libprefix = 'cyg'
|
10
|
+
libsuffix = 'dll'
|
11
|
+
when /mingw/, /mswin/
|
12
|
+
libprefix = ''
|
13
|
+
libsuffix = 'dll'
|
14
|
+
when /darwin/
|
15
|
+
libsuffix = 'dylib'
|
16
|
+
end
|
17
|
+
|
18
|
+
LIBPREFIX = libprefix || 'lib'
|
19
|
+
LIBSUFFIX = libsuffix || 'so'
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def find_libpython(python = nil)
|
23
|
+
debug_report("find_libpython(#{python.inspect})")
|
24
|
+
if python
|
25
|
+
begin
|
26
|
+
python_config = investigate_python_config(python)
|
27
|
+
rescue
|
28
|
+
raise ::PyCall::PythonNotFound
|
29
|
+
end
|
30
|
+
else
|
31
|
+
%w[python python3].each do |python_cmd|
|
32
|
+
begin
|
33
|
+
python_config = investigate_python_config(python_cmd)
|
34
|
+
python = python_cmd
|
35
|
+
break
|
36
|
+
rescue
|
37
|
+
raise ::PyCall::PythonNotFound
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
libs = make_libs(python_config)
|
43
|
+
libpaths = make_libpaths(python_config)
|
44
|
+
|
45
|
+
# Try LIBPYTHON environment variable first.
|
46
|
+
if (libpython = ENV['LIBPYTHON'])
|
47
|
+
if File.file?(libpython)
|
48
|
+
begin
|
49
|
+
return dlopen(libpython)
|
50
|
+
rescue Fiddle::DLError
|
51
|
+
debug_report "#{$!.class}: #{$!.message}"
|
52
|
+
else
|
53
|
+
debug_report "Success to dlopen #{libpython.inspect} from ENV['LIBPYTHON']"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
warn "WARNING(#{self}.#{__method__}) Ignore the wrong libpython location specified in ENV['LIBPYTHON']."
|
57
|
+
end
|
58
|
+
|
59
|
+
# Find libpython (we hope):
|
60
|
+
multiarch = python_config[:MULTIARCH] || python_config[:multiarch]
|
61
|
+
libs.each do |lib|
|
62
|
+
libpaths.each do |libpath|
|
63
|
+
libpath_libs = [ File.join(libpath, lib) ]
|
64
|
+
libpath_libs << File.join(libpath, multiarch, lib) if multiarch
|
65
|
+
libpath_libs.each do |libpath_lib|
|
66
|
+
[ libpath_lib, "#{libpath_lib}.#{LIBSUFFIX}" ].each do |fullname|
|
67
|
+
unless File.file? fullname
|
68
|
+
debug_report "Unable to find #{fullname}"
|
69
|
+
next
|
70
|
+
end
|
71
|
+
begin
|
72
|
+
return dlopen(libpath_lib)
|
73
|
+
rescue Fiddle::DLError
|
74
|
+
debug_report "#{$!.class}: #{$!.message}"
|
75
|
+
else
|
76
|
+
debug_report "Success to dlopen #{libpaht_lib}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Find libpython in the system path
|
84
|
+
libs.each do |lib|
|
85
|
+
begin
|
86
|
+
return dlopen(lib)
|
87
|
+
rescue Fiddle::DLError
|
88
|
+
debug_report "#{$!.class}: #{$!.message}"
|
89
|
+
else
|
90
|
+
debug_report "Success to dlopen #{lib}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
raise ::PyCall::PythonNotFound
|
95
|
+
end
|
96
|
+
|
97
|
+
def investigate_python_config(python)
|
98
|
+
python_env = { 'PYTHONIOENCODING' => 'UTF-8' }
|
99
|
+
debug_report("investigate_python_config(#{python.inspect})")
|
100
|
+
IO.popen(python_env, [python, python_investigator_py], 'r') do |io|
|
101
|
+
{}.tap do |config|
|
102
|
+
io.each_line do |line|
|
103
|
+
key, value = line.chomp.split(': ', 2)
|
104
|
+
config[key.to_sym] = value if value != 'None'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
rescue Errno::ENOENT
|
109
|
+
raise PyCall::PythonInvestigationFailed
|
110
|
+
end
|
111
|
+
|
112
|
+
def python_investigator_py
|
113
|
+
File.expand_path('../../python/investigator.py', __FILE__)
|
114
|
+
end
|
115
|
+
|
116
|
+
def make_libs(python_config)
|
117
|
+
libs = []
|
118
|
+
%i(INSTSONAME LDLIBRARY).each do |key|
|
119
|
+
lib = python_config[key]
|
120
|
+
libs << lib << File.basename(lib) if lib
|
121
|
+
end
|
122
|
+
if (lib = python_config[:LIBRARY])
|
123
|
+
libs << File.basename(lib, File.extname(lib))
|
124
|
+
end
|
125
|
+
|
126
|
+
v = python_config[:VERSION]
|
127
|
+
libs << "#{LIBPREFIX}python#{v}" << "#{LIBPREFIX}python"
|
128
|
+
libs.uniq!
|
129
|
+
|
130
|
+
debug_report "libs: #{libs.inspect}"
|
131
|
+
return libs
|
132
|
+
end
|
133
|
+
|
134
|
+
def make_libpaths(python_config)
|
135
|
+
executable = python_config[:executable]
|
136
|
+
libpaths = [ python_config[:LIBDIR] ]
|
137
|
+
if Fiddle::WINDOWS
|
138
|
+
libpaths << File.dirname(executable)
|
139
|
+
else
|
140
|
+
libpaths << File.expand_path('../../lib', executable)
|
141
|
+
end
|
142
|
+
libpaths << python_config[:PYTHONFRAMEWORKPREFIX]
|
143
|
+
exec_prefix = python_config[:exec_prefix]
|
144
|
+
libpaths << exec_prefix << File.join(exec_prefix, 'lib')
|
145
|
+
libpaths.compact!
|
146
|
+
|
147
|
+
debug_report "libpaths: #{libpaths.inspect}"
|
148
|
+
return libpaths
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def dlopen(libname)
|
154
|
+
Fiddle.dlopen(libname).tap do |handle|
|
155
|
+
debug_report("dlopen(#{libname.inspect}) = #{handle.inspect}") if handle
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def debug_report(message)
|
160
|
+
return unless debug?
|
161
|
+
$stderr.puts "DEBUG(find_libpython) #{message}"
|
162
|
+
end
|
163
|
+
|
164
|
+
def debug?
|
165
|
+
@debug ||= (ENV['PYCALL_DEBUG_FIND_LIBPYTHON'] == '1')
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module PyCall
|
4
|
+
module LibPython
|
5
|
+
class PyObjectStruct < FFI::Struct
|
6
|
+
end
|
7
|
+
|
8
|
+
class PyTypeObjectStruct < PyObjectStruct
|
9
|
+
end
|
10
|
+
|
11
|
+
class PyObjectStruct < FFI::Struct
|
12
|
+
layout ob_refcnt: :ssize_t,
|
13
|
+
ob_type: PyTypeObjectStruct.by_ref
|
14
|
+
|
15
|
+
def self.null
|
16
|
+
new(FFI::Pointer::NULL)
|
17
|
+
end
|
18
|
+
|
19
|
+
def py_none?
|
20
|
+
PyCall.none?(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def kind_of?(klass)
|
24
|
+
klass = klass.__pyobj__ if klass.kind_of? PyObjectWrapper
|
25
|
+
return super unless klass.kind_of? PyObjectStruct
|
26
|
+
PyCall::Types.pyisinstance(self, klass)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
require 'pycall/libpython/pyobject_struct'
|
2
|
+
|
3
|
+
module PyCall
|
4
|
+
module LibPython
|
5
|
+
# types:
|
6
|
+
T_SHORT = 0
|
7
|
+
T_INT = 1
|
8
|
+
T_LONG = 2
|
9
|
+
T_FLOAT = 3
|
10
|
+
T_DOUBLE = 4
|
11
|
+
T_STRING = 5
|
12
|
+
T_OBJECT = 6
|
13
|
+
T_CHAR = 7
|
14
|
+
T_BYTE = 8
|
15
|
+
T_UBYTE = 9
|
16
|
+
T_USHORT = 10
|
17
|
+
T_UINT = 11
|
18
|
+
T_ULONG = 12
|
19
|
+
T_STRING_INPLACE = 13
|
20
|
+
T_BOOL = 14
|
21
|
+
T_OBJECT_EX = 16
|
22
|
+
T_LONGLONG = 17 # added in Python 2.5
|
23
|
+
T_ULONGLONG = 18 # added in Python 2.5
|
24
|
+
T_PYSSIZET = 19 # added in Python 2.6
|
25
|
+
T_NONE = 20 # added in Python 3.0
|
26
|
+
|
27
|
+
# flags:
|
28
|
+
READONLY = 1
|
29
|
+
READ_RESTRICTED = 2
|
30
|
+
PY_WRITE_RESTRICTED = 4
|
31
|
+
RESTRICTED = (READ_RESTRICTED | PY_WRITE_RESTRICTED)
|
32
|
+
|
33
|
+
# Python 2.7
|
34
|
+
Py_TPFLAGS_HAVE_GETCHARBUFFER = 0x00000001<<0
|
35
|
+
Py_TPFLAGS_HAVE_SEQUENCE_IN = 0x00000001<<1
|
36
|
+
Py_TPFLAGS_GC = 0 # was sometimes (0x00000001<<2) in Python <= 2.1
|
37
|
+
Py_TPFLAGS_HAVE_INPLACEOPS = 0x00000001<<3
|
38
|
+
Py_TPFLAGS_CHECKTYPES = 0x00000001<<4
|
39
|
+
Py_TPFLAGS_HAVE_RICHCOMPARE = 0x00000001<<5
|
40
|
+
Py_TPFLAGS_HAVE_WEAKREFS = 0x00000001<<6
|
41
|
+
Py_TPFLAGS_HAVE_ITER = 0x00000001<<7
|
42
|
+
Py_TPFLAGS_HAVE_CLASS = 0x00000001<<8
|
43
|
+
Py_TPFLAGS_HAVE_INDEX = 0x00000001<<17
|
44
|
+
Py_TPFLAGS_HAVE_NEWBUFFER = 0x00000001<<21
|
45
|
+
Py_TPFLAGS_STRING_SUBCLASS = 0x00000001<<27
|
46
|
+
|
47
|
+
# Python 3.0+ has only these:
|
48
|
+
Py_TPFLAGS_HEAPTYPE = 0x00000001<<9
|
49
|
+
Py_TPFLAGS_BASETYPE = 0x00000001<<10
|
50
|
+
Py_TPFLAGS_READY = 0x00000001<<12
|
51
|
+
Py_TPFLAGS_READYING = 0x00000001<<13
|
52
|
+
Py_TPFLAGS_HAVE_GC = 0x00000001<<14
|
53
|
+
Py_TPFLAGS_HAVE_VERSION_TAG = 0x00000001<<18
|
54
|
+
Py_TPFLAGS_VALID_VERSION_TAG = 0x00000001<<19
|
55
|
+
Py_TPFLAGS_IS_ABSTRACT = 0x00000001<<20
|
56
|
+
Py_TPFLAGS_INT_SUBCLASS = 0x00000001<<23
|
57
|
+
Py_TPFLAGS_LONG_SUBCLASS = 0x00000001<<24
|
58
|
+
Py_TPFLAGS_LIST_SUBCLASS = 0x00000001<<25
|
59
|
+
Py_TPFLAGS_TUPLE_SUBCLASS = 0x00000001<<26
|
60
|
+
Py_TPFLAGS_BYTES_SUBCLASS = 0x00000001<<27
|
61
|
+
Py_TPFLAGS_UNICODE_SUBCLASS = 0x00000001<<28
|
62
|
+
Py_TPFLAGS_DICT_SUBCLASS = 0x00000001<<29
|
63
|
+
Py_TPFLAGS_BASE_EXC_SUBCLASS = 0x00000001<<30
|
64
|
+
Py_TPFLAGS_TYPE_SUBCLASS = 0x00000001<<31
|
65
|
+
|
66
|
+
# only use this if we have the stackless extension
|
67
|
+
Py_TPFLAGS_HAVE_STACKLESS_EXTENSION_ = 0x00000003<<15
|
68
|
+
|
69
|
+
class PyMethodDef < FFI::Struct
|
70
|
+
layout ml_name: :string,
|
71
|
+
ml_meth: :pointer,
|
72
|
+
ml_flags: :int,
|
73
|
+
ml_doc: :string # may be NULL
|
74
|
+
|
75
|
+
def initialize(*args)
|
76
|
+
case args.length
|
77
|
+
when 3, 4
|
78
|
+
name, meth, flags, doc = *args
|
79
|
+
super()
|
80
|
+
self.ml_name = name
|
81
|
+
self[:ml_meth] = meth
|
82
|
+
self[:ml_flags] = flags
|
83
|
+
self.ml_doc = doc
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def ml_name=(str)
|
90
|
+
@saved_name = FFI::MemoryPointer.from_string(str || '')
|
91
|
+
self.pointer.put_pointer(offset_of(:ml_name), @saved_name)
|
92
|
+
end
|
93
|
+
|
94
|
+
def ml_doc=(str)
|
95
|
+
@saved_doc = FFI::MemoryPointer.from_string(str || '')
|
96
|
+
self.pointer.put_pointer(offset_of(:ml_name), @saved_doc)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# ml_flags should be one of:
|
101
|
+
METH_VARARGS = 0x0001 # args are a tuple of arguments
|
102
|
+
METH_KEYWORDS = 0x0002 # two arguments: the varargs and the kwargs
|
103
|
+
METH_NOARGS = 0x0004 # no arguments (NULL argument pointer)
|
104
|
+
METH_O = 0x0008 # single argument (not wrapped in tuple)
|
105
|
+
|
106
|
+
# not sure when these are needed:
|
107
|
+
METH_CLASS = 0x0010 # for class methods
|
108
|
+
METH_STATIC = 0x0020 # for static methods
|
109
|
+
|
110
|
+
class PyGetSetDef < FFI::Struct
|
111
|
+
layout name: :string,
|
112
|
+
get: :pointer,
|
113
|
+
set: :pointer, # may be NULL for read-only members
|
114
|
+
doc: :string,
|
115
|
+
closure: :pointer
|
116
|
+
end
|
117
|
+
|
118
|
+
class PyMemberDef < FFI::Struct
|
119
|
+
layout name: :string,
|
120
|
+
type: :int,
|
121
|
+
offset: :ssize_t,
|
122
|
+
flags: :int,
|
123
|
+
doc: :string
|
124
|
+
|
125
|
+
[:name, :doc].each do |field|
|
126
|
+
define_method(:"#{field}=") do |str|
|
127
|
+
saved_str = FFI::MemoryPointer.from_string(str)
|
128
|
+
instance_variable_set(:"@saved_#{field}", saved_str)
|
129
|
+
self.pointer.put_pointer(offset_of(field), saved_str)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class PyTypeObjectStruct < PyObjectStruct
|
135
|
+
layout ob_refcnt: :ssize_t,
|
136
|
+
ob_type: PyTypeObjectStruct.by_ref,
|
137
|
+
ob_size: :ssize_t,
|
138
|
+
|
139
|
+
tp_name: :string, # For printing, in format "<module>.<name>"
|
140
|
+
|
141
|
+
# For allocation
|
142
|
+
tp_basicsize: :ssize_t,
|
143
|
+
tp_itemsize: :ssize_t,
|
144
|
+
|
145
|
+
# Methods to implement standard operations
|
146
|
+
|
147
|
+
tp_dealloc: :pointer,
|
148
|
+
tp_print: :pointer,
|
149
|
+
tp_getattr: :pointer,
|
150
|
+
tp_setattr: :pointer,
|
151
|
+
tp_as_async: :pointer, # formerly known as tp_compare (Python 2) or tp_reserved (Python 3)
|
152
|
+
tp_repr: :pointer,
|
153
|
+
|
154
|
+
# Method suites for standard classes
|
155
|
+
|
156
|
+
tp_as_number: :pointer,
|
157
|
+
tp_as_sequence: :pointer,
|
158
|
+
tp_as_mapping: :pointer,
|
159
|
+
|
160
|
+
# More standard operations (here for binary compatibility)
|
161
|
+
|
162
|
+
tp_hash: :pointer,
|
163
|
+
tp_call: :pointer,
|
164
|
+
tp_str: :pointer,
|
165
|
+
tp_getattro: :pointer,
|
166
|
+
tp_setattro: :pointer,
|
167
|
+
|
168
|
+
# Functions to access object as input/output buffer
|
169
|
+
tp_as_buffer: :pointer,
|
170
|
+
|
171
|
+
# Flags to define presence of optional/expanded features
|
172
|
+
tp_flags: :ulong,
|
173
|
+
|
174
|
+
tp_doc: :string, # Documentation string
|
175
|
+
|
176
|
+
# Assigned meaning in release 2.0
|
177
|
+
# call function for all accessible objects
|
178
|
+
tp_traverse: :pointer,
|
179
|
+
|
180
|
+
# delete references to contained objects
|
181
|
+
tp_clear: :pointer,
|
182
|
+
|
183
|
+
# Assigned meaning in release 2.1
|
184
|
+
# rich comparisons
|
185
|
+
tp_richcompare: :pointer,
|
186
|
+
|
187
|
+
# weak reference enabler
|
188
|
+
tp_weaklistoffset: :ssize_t,
|
189
|
+
|
190
|
+
# Iterators
|
191
|
+
tp_iter: :pointer,
|
192
|
+
tp_iternext: :pointer,
|
193
|
+
|
194
|
+
# Attribute descriptor and subclassing stuff
|
195
|
+
tp_methods: PyMethodDef.by_ref,
|
196
|
+
tp_members: PyMemberDef.by_ref,
|
197
|
+
tp_getset: PyGetSetDef.by_ref,
|
198
|
+
tp_base: :pointer,
|
199
|
+
tp_dict: PyObjectStruct.by_ref,
|
200
|
+
tp_descr_get: :pointer,
|
201
|
+
tp_descr_set: :pointer,
|
202
|
+
tp_dictoffset: :ssize_t,
|
203
|
+
tp_init: :pointer,
|
204
|
+
tp_alloc: :pointer,
|
205
|
+
tp_new: :pointer,
|
206
|
+
tp_free: :pointer, # Low-level free-memory routine
|
207
|
+
tp_is_gc: :pointer, # For PyObject_IS_GC
|
208
|
+
tp_bases: PyObjectStruct.by_ref,
|
209
|
+
tp_mro: PyObjectStruct.by_ref, # method resolution order
|
210
|
+
tp_cache: PyObjectStruct.by_ref,
|
211
|
+
tp_subclasses: PyObjectStruct.by_ref,
|
212
|
+
tp_weaklist: PyObjectStruct.by_ref,
|
213
|
+
tp_del: :pointer,
|
214
|
+
|
215
|
+
# Type attribute cache version tag. Added in version 2.6
|
216
|
+
tp_version_tag: :uint,
|
217
|
+
|
218
|
+
tp_finalize: :pointer,
|
219
|
+
|
220
|
+
# The following members are only used for COUNT_ALLOCS builds of Python
|
221
|
+
tp_allocs: :ssize_t,
|
222
|
+
tp_frees: :ssize_t,
|
223
|
+
tp_maxalloc: :ssize_t,
|
224
|
+
tp_prev: :pointer,
|
225
|
+
tp_next: :pointer
|
226
|
+
|
227
|
+
def self.new(*args)
|
228
|
+
case args.length
|
229
|
+
when 0, 1
|
230
|
+
super
|
231
|
+
else
|
232
|
+
name, basic_size = *args
|
233
|
+
new.tap do |t|
|
234
|
+
# NOTE: Disable autorelease for avoiding SEGV occurrance in Python's GC collect function
|
235
|
+
# at which the __new__ method object of this type object is freed.
|
236
|
+
t.pointer.autorelease = false
|
237
|
+
|
238
|
+
# PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
239
|
+
t[:ob_refcnt] = 1
|
240
|
+
t[:ob_type] = LibPython.PyType_Type
|
241
|
+
t[:ob_size] = 0
|
242
|
+
|
243
|
+
t[:tp_basicsize] = basic_size
|
244
|
+
stackless_extension_flag = PyCall.has_stackless_extension ? Py_TPFLAGS_HAVE_STACKLESS_EXTENSION_ : 0
|
245
|
+
t[:tp_flags] = if PYTHON_VERSION >= '3'
|
246
|
+
stackless_extension_flag | Py_TPFLAGS_HAVE_VERSION_TAG
|
247
|
+
else
|
248
|
+
Py_TPFLAGS_HAVE_GETCHARBUFFER |
|
249
|
+
Py_TPFLAGS_HAVE_SEQUENCE_IN |
|
250
|
+
Py_TPFLAGS_HAVE_INPLACEOPS |
|
251
|
+
Py_TPFLAGS_HAVE_RICHCOMPARE |
|
252
|
+
Py_TPFLAGS_HAVE_WEAKREFS |
|
253
|
+
Py_TPFLAGS_HAVE_ITER |
|
254
|
+
Py_TPFLAGS_HAVE_CLASS |
|
255
|
+
stackless_extension_flag |
|
256
|
+
Py_TPFLAGS_HAVE_INDEX
|
257
|
+
end
|
258
|
+
t.tp_name = name
|
259
|
+
yield t if block_given?
|
260
|
+
t[:tp_new] = LibPython.find_symbol(:PyType_GenericNew) if t[:tp_new] == FFI::Pointer::NULL
|
261
|
+
raise PyError.fetch if LibPython.PyType_Ready(t) < 0
|
262
|
+
LibPython.Py_IncRef(t)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def tp_name=(str)
|
268
|
+
@saved_name = FFI::MemoryPointer.from_string(str)
|
269
|
+
self.pointer.put_pointer(offset_of(:tp_name), @saved_name)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|