pycall 1.0.1-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +41 -0
  5. data/CHANGES.md +39 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +91 -0
  9. data/Rakefile +29 -0
  10. data/appveyor.yml +138 -0
  11. data/bin/console +10 -0
  12. data/bin/guard +17 -0
  13. data/bin/rspec +17 -0
  14. data/bin/runner +6 -0
  15. data/bin/setup +8 -0
  16. data/config/Guardfile +30 -0
  17. data/docker/Dockerfile +191 -0
  18. data/docker/Gemfile +12 -0
  19. data/docker/README.md +22 -0
  20. data/examples/classifier_comparison.rb +135 -0
  21. data/examples/datascience_rb_20170519.ipynb +4836 -0
  22. data/examples/hist.rb +32 -0
  23. data/examples/notebooks/classifier_comparison.ipynb +226 -0
  24. data/examples/notebooks/forest_importances.ipynb +238 -0
  25. data/examples/notebooks/iruby_integration.ipynb +183 -0
  26. data/examples/notebooks/lorenz_attractor.ipynb +214 -0
  27. data/examples/notebooks/polar_axes.ipynb +209 -0
  28. data/examples/notebooks/sum_benchmarking.ipynb +374 -0
  29. data/examples/notebooks/xkcd_style.ipynb +149 -0
  30. data/examples/plot_forest_importances_faces.rb +46 -0
  31. data/examples/sum_benchmarking.rb +49 -0
  32. data/ext/pycall/extconf.rb +3 -0
  33. data/ext/pycall/gc.c +74 -0
  34. data/ext/pycall/libpython.c +217 -0
  35. data/ext/pycall/pycall.c +2184 -0
  36. data/ext/pycall/pycall_internal.h +700 -0
  37. data/ext/pycall/range.c +69 -0
  38. data/ext/pycall/ruby_wrapper.c +432 -0
  39. data/lib/pycall.rb +91 -0
  40. data/lib/pycall/conversion.rb +173 -0
  41. data/lib/pycall/dict.rb +48 -0
  42. data/lib/pycall/error.rb +10 -0
  43. data/lib/pycall/gc_guard.rb +84 -0
  44. data/lib/pycall/import.rb +120 -0
  45. data/lib/pycall/init.rb +55 -0
  46. data/lib/pycall/iruby_helper.rb +40 -0
  47. data/lib/pycall/libpython.rb +12 -0
  48. data/lib/pycall/libpython/finder.rb +170 -0
  49. data/lib/pycall/libpython/pyobject_struct.rb +30 -0
  50. data/lib/pycall/libpython/pytypeobject_struct.rb +273 -0
  51. data/lib/pycall/list.rb +45 -0
  52. data/lib/pycall/pretty_print.rb +9 -0
  53. data/lib/pycall/pyerror.rb +30 -0
  54. data/lib/pycall/pyobject_wrapper.rb +212 -0
  55. data/lib/pycall/python/PyCall/__init__.py +1 -0
  56. data/lib/pycall/python/PyCall/six.py +23 -0
  57. data/lib/pycall/python/investigator.py +7 -0
  58. data/lib/pycall/pytypeobject_wrapper.rb +90 -0
  59. data/lib/pycall/set.rb +19 -0
  60. data/lib/pycall/slice.rb +8 -0
  61. data/lib/pycall/tuple.rb +46 -0
  62. data/lib/pycall/version.rb +3 -0
  63. data/lib/pycall/wrapper_object_cache.rb +61 -0
  64. data/pycall.gemspec +40 -0
  65. data/tasks/docker.rake +21 -0
  66. data/tasks/pycall.rake +7 -0
  67. metadata +228 -0
@@ -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,12 @@
1
+ module PyCall
2
+ module LibPython
3
+ require 'pycall/libpython/finder'
4
+
5
+ def self.handle
6
+ # NOTE: PyCall.init redefine this method.
7
+ # See pycall/init.rb for the detail.
8
+ PyCall.init
9
+ handle
10
+ end
11
+ end
12
+ 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