pycall 1.0.1-x64-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|