def add_enum(gtype, a, b): nickname = type_name(gtype) all_enums.append(nickname) type_map(gtype, add_enum) return ffi.NULL
def add_enum(gtype, a, b): nickname = type_name(gtype) all_nicknames.append(nickname) gtype_to_js_param[gtype] = f'{remove_prefix(nickname)} | Enum' type_map(gtype, add_enum) return ffi.NULL
def generate_operation(operation_name): intro = Introspect.get(operation_name) result = ' * @method ' if intro.member_x is None: result += 'static ' if len(intro.required_output) == 0: result += 'void ' elif len(intro.required_output) == 1: arg = intro.required_output[0] details = intro.details[arg] result += '{0} '.format(gtype_to_php(details['type'], True)) else: # we generate a Returns: block for this case, see below result += 'array ' result += '{0}('.format(operation_name) for name in intro.method_args: details = intro.details[name] result += '{0} ${1}, '.format(gtype_to_php(details['type']), name) result += 'array $options = []) ' description = intro.description result += description[0].upper() + description[1:] + '.\n' # find any Enums we've referenced and output @see lines for them for name in intro.required_output + intro.method_args: details = intro.details[name] fundamental = gobject_lib.g_type_fundamental(details['type']) if fundamental != GValue.genum_type: continue result += ' * @see {0} for possible values for ${1}\n'.format( remove_prefix(type_name(details['type'])), name) if len(intro.required_output) > 1: result += ' * Return array with: [\n' for name in intro.required_output: details = intro.details[name] result += ' * \'{0}\' => @type {1} {2}\n'.format( name, gtype_to_php(details['type']), details['blurb'][0].upper() + details['blurb'][1:]) result += ' * ];\n' result += ' * @throws Exception\n' return result
def to_enum(gtype, value): """Turn a string into an enum value ready to be passed into libvips. """ if isinstance(value, basestring if _is_PY2 else str): enum_value = vips_lib.vips_enum_from_nick(b'pyvips', gtype, _to_bytes(value)) if enum_value < 0: raise Error('no value {0} in gtype {1} ({2})'.format( value, type_name(gtype), gtype)) else: enum_value = value return enum_value
def get_cpp_type(gtype): """Map a gtype to C++ type name we use to represent it. """ if gtype in gtype_to_cpp: return gtype_to_cpp[gtype] fundamental = gobject_lib.g_type_fundamental(gtype) # enum params use the C name as their name if fundamental == GValue.genum_type: return type_name(gtype) if fundamental in gtype_to_cpp: return gtype_to_cpp[fundamental] return '<unknown type>'
def get_cpp_type(gtype): """Map a gtype to C++ type name we use to represent it. """ if gtype in gtype_to_cpp: return gtype_to_cpp[gtype] fundamental = gobject_lib.g_type_fundamental(gtype) # enum params use the C name as their name if fundamental == GValue.genum_type: return type_name(gtype) if fundamental in gtype_to_cpp: return gtype_to_cpp[fundamental] return '<unknown type>'
def gtype_to_python(gtype): """Map a gtype to the name of the Python type we use to represent it. """ fundamental = gobject_lib.g_type_fundamental(gtype) # enums can be strings or class members ... we want to generate a union # type if fundamental == GValue.genum_type: name = type_name(gtype) if name.startswith('Vips'): name = name[4:] return "Union[str, %s]" % name if gtype in GValue._gtype_to_python: return GValue._gtype_to_python[gtype] if fundamental in GValue._gtype_to_python: return GValue._gtype_to_python[fundamental] return '<unknown type>'
def new_pointer_from_gtype(gtype): """Make a new GObject pointer from a gtype. This is useful for subclasses which need to control the construction process. You can pass the result pointer to the Python constructor for the object you are building. You will need to call VipsObject.build() to finish construction. Returns: A pointer to a new GObject. Raises: :class:`.Error` """ pointer = gobject_lib.g_object_new(gtype, ffi.NULL) if pointer == ffi.NULL: raise Error("can't create {0}".format(type_name(gtype))) return pointer
def get(self): """Get the contents of a GValue. The contents of the GValue are read out as a Python type. """ # logger.debug('GValue.get: self = %s', self) gtype = self.gvalue.g_type fundamental = gobject_lib.g_type_fundamental(gtype) result = None if gtype == GValue.gbool_type: result = bool(gobject_lib.g_value_get_boolean(self.gvalue)) elif gtype == GValue.gint_type: result = gobject_lib.g_value_get_int(self.gvalue) elif gtype == GValue.gdouble_type: result = gobject_lib.g_value_get_double(self.gvalue) elif fundamental == GValue.genum_type: return GValue.from_enum(gtype, gobject_lib.g_value_get_enum(self.gvalue)) elif fundamental == GValue.gflags_type: result = gobject_lib.g_value_get_flags(self.gvalue) elif gtype == GValue.gstr_type: pointer = gobject_lib.g_value_get_string(self.gvalue) if pointer != ffi.NULL: result = _to_string(pointer) elif gtype == GValue.refstr_type: psize = ffi.new('size_t *') pointer = vips_lib.vips_value_get_ref_string(self.gvalue, psize) # psize[0] will be number of bytes in string, but just assume it's # NULL-terminated result = _to_string(pointer) elif gtype == GValue.image_type: # g_value_get_object() will not add a ref ... that is # held by the gvalue go = gobject_lib.g_value_get_object(self.gvalue) vi = ffi.cast('VipsImage *', go) # we want a ref that will last with the life of the vimage: # this ref is matched by the unref that's attached to finalize # by Image() gobject_lib.g_object_ref(go) result = pyvips.Image(vi) elif gtype == GValue.array_int_type: pint = ffi.new('int *') array = vips_lib.vips_value_get_array_int(self.gvalue, pint) result = [] for i in range(0, pint[0]): result.append(array[i]) elif gtype == GValue.array_double_type: pint = ffi.new('int *') array = vips_lib.vips_value_get_array_double(self.gvalue, pint) result = [] for i in range(0, pint[0]): result.append(array[i]) elif gtype == GValue.array_image_type: pint = ffi.new('int *') array = vips_lib.vips_value_get_array_image(self.gvalue, pint) result = [] for i in range(0, pint[0]): vi = array[i] gobject_lib.g_object_ref(vi) image = pyvips.Image(vi) result.append(image) elif gtype == GValue.blob_type: psize = ffi.new('size_t *') array = vips_lib.vips_value_get_blob(self.gvalue, psize) buf = ffi.cast('char*', array) result = ffi.unpack(buf, psize[0]) else: raise Error('unsupported gtype for get {0}'.format( type_name(gtype))) return result
def set(self, value): """Set a GValue. The value is converted to the type of the GValue, if possible, and assigned. """ # logger.debug('GValue.set: value = %s', value) gtype = self.gvalue.g_type fundamental = gobject_lib.g_type_fundamental(gtype) if gtype == GValue.gbool_type: gobject_lib.g_value_set_boolean(self.gvalue, value) elif gtype == GValue.gint_type: gobject_lib.g_value_set_int(self.gvalue, int(value)) elif gtype == GValue.gdouble_type: gobject_lib.g_value_set_double(self.gvalue, value) elif fundamental == GValue.genum_type: gobject_lib.g_value_set_enum(self.gvalue, GValue.to_enum(gtype, value)) elif fundamental == GValue.gflags_type: gobject_lib.g_value_set_flags(self.gvalue, value) elif gtype == GValue.gstr_type: gobject_lib.g_value_set_string(self.gvalue, _to_bytes(value)) elif gtype == GValue.refstr_type: vips_lib.vips_value_set_ref_string(self.gvalue, _to_bytes(value)) elif fundamental == GValue.gobject_type: gobject_lib.g_value_set_object(self.gvalue, value.pointer) elif gtype == GValue.array_int_type: if isinstance(value, numbers.Number): value = [value] array = ffi.new('int[]', value) vips_lib.vips_value_set_array_int(self.gvalue, array, len(value)) elif gtype == GValue.array_double_type: if isinstance(value, numbers.Number): value = [value] array = ffi.new('double[]', value) vips_lib.vips_value_set_array_double(self.gvalue, array, len(value)) elif gtype == GValue.array_image_type: if isinstance(value, pyvips.Image): value = [value] vips_lib.vips_value_set_array_image(self.gvalue, len(value)) array = vips_lib.vips_value_get_array_image(self.gvalue, ffi.NULL) for i, image in enumerate(value): gobject_lib.g_object_ref(image.pointer) array[i] = image.pointer elif gtype == GValue.blob_type: # we need to set the blob to a copy of the string that vips_lib # can own memory = glib_lib.g_malloc(len(value)) ffi.memmove(memory, value, len(value)) # this is horrible! # # * in API mode, we must have 8.6+ and use set_blob_free to # attach the metadata to avoid leaks # * pre-8.6, we just pass a NULL free pointer and live with the # leak # # this is because in API mode you can't pass a builtin (what # vips_lib.g_free() becomes) as a parameter to ffi.callback(), and # vips_value_set_blob() needs a callback for arg 2 # # additionally, you can't make a py def which calls g_free() and # then use the py def in the callback, since libvips will trigger # these functions during cleanup, and py will have shut down by # then and you'll get a segv if at_least_libvips(8, 6): vips_lib.vips_value_set_blob_free(self.gvalue, memory, len(value)) else: if pyvips.API_mode: vips_lib.vips_value_set_blob(self.gvalue, ffi.NULL, memory, len(value)) else: vips_lib.vips_value_set_blob(self.gvalue, glib_lib.g_free, memory, len(value)) else: raise Error( 'unsupported gtype for set {0}, fundamental {1}'.format( type_name(gtype), type_name(fundamental)))
def generate_operation(operation_name): op = Operation.new_from_name(operation_name) # we are only interested in non-deprecated args args = [[name, flags] for name, flags in op.get_args() if not flags & _DEPRECATED] # find the first required input image arg, if any ... that will be self member_x = None for name, flags in args: if ((flags & _INPUT) != 0 and (flags & _REQUIRED) != 0 and op.get_typeof(name) == GValue.image_type): member_x = name break required_input = [ name for name, flags in args if (flags & _INPUT) != 0 and (flags & _REQUIRED) != 0 and name != member_x ] required_output = [ name for name, flags in args if ((flags & _OUTPUT) != 0 and (flags & _REQUIRED) != 0) or ( (flags & _INPUT) != 0 and (flags & _REQUIRED) != 0 and (flags & _MODIFY) != 0) ] result = ' * @method ' if member_x is None: result += 'static ' if len(required_output) == 0: result += 'void ' elif len(required_output) == 1: result += '{0} '.format( gtype_to_php(op.get_typeof(required_output[0]), True)) else: # we generate a Returns: block for this case, see below result += 'array ' result += '{0}('.format(operation_name) for name in required_input: gtype = op.get_typeof(name) result += '{0} ${1}, '.format(gtype_to_php(gtype), name) result += 'array $options = []) ' description = op.get_description() result += description[0].upper() + description[1:] + '.\n' # find any Enums we've referenced and output @see lines for them for name in required_output + required_input: gtype = op.get_typeof(name) fundamental = gobject_lib.g_type_fundamental(gtype) if fundamental != GValue.genum_type: continue result += ' * @see {0} for possible values for ${1}\n'.format( remove_prefix(type_name(gtype)), name) if len(required_output) > 1: result += ' * Return array with: [\n' for name in required_output: gtype = op.get_typeof(name) blurb = op.get_blurb(name) result += ' * \'{0}\' => @type {1} {2}\n'.format( name, gtype_to_php(gtype), blurb[0].upper() + blurb[1:]) result += ' * ];\n' result += ' * @throws Exception\n' return result
def generate_auto_doc(filename): all_nicknames = [] def add_nickname(gtype, a, b): nickname = nickname_find(gtype) try: # can fail for abstract types op = Operation.new_from_name(nickname) # we are only interested in non-deprecated operations if (op.get_flags() & _OPERATION_DEPRECATED) == 0: all_nicknames.append(nickname) except Error: pass type_map(gtype, add_nickname) return ffi.NULL type_map(type_from_name('VipsOperation'), add_nickname) # add 'missing' synonyms by hand all_nicknames.append('crop') # make list unique and sort all_nicknames = list(set(all_nicknames)) all_nicknames.sort() # these have hand-written methods, don't autodoc them no_generate = [ 'bandjoin', 'bandrank', 'ifthenelse', 'add', 'subtract', 'multiply', 'divide', 'remainder' ] all_nicknames = [x for x in all_nicknames if x not in no_generate] print('Generating {0} ...'.format(filename)) with open(filename, 'w') as f: f.write(preamble) f.write('\n') f.write('namespace Jcupitt\\Vips;\n') f.write('\n') f.write('/**\n') f.write(' * Autodocs for the Image class.\n') f.write(class_header) f.write(' *\n') for nickname in all_nicknames: f.write(generate_operation(nickname)) f.write(' *\n') # all magic properties tmp_file = Image.new_temp_file('%s.v') all_properties = tmp_file.get_fields() for name in all_properties: php_name = name.replace('-', '_') gtype = tmp_file.get_typeof(name) fundamental = gobject_lib.g_type_fundamental(gtype) f.write(' * @property {0} ${1} {2}\n'.format( gtype_to_php(gtype), php_name, tmp_file.get_blurb(name))) if fundamental == GValue.genum_type: f.write(' * @see {0} for possible values\n'.format( remove_prefix(type_name(gtype)))) f.write(' */\n') f.write('abstract class ImageAutodoc\n') f.write('{\n') f.write('}\n')
def generate_operation(operation_name, declaration_only=False): intro = Introspect.get(operation_name) required_output = [ name for name in intro.required_output if name != intro.member_x ] has_input = len(intro.method_args) >= 1 has_output = len(required_output) >= 1 has_optional_options = len(intro.optional_input) + len( intro.optional_output) >= 1 # Add a C++ style comment block with some additional markings (@param, # @return) if declaration_only: result = f'\n/**\n * {intro.description.capitalize()}.' for name in intro.method_args: result += f"\n * @param {cppize(name)} {intro.details[name]['blurb']}." if has_output: # skip the first element for name in required_output[1:]: result += f"\n * @param {cppize(name)} {intro.details[name]['blurb']}." if has_optional_options: result += '\n * @param js_options Optional options.' if has_output: result += f"\n * @return {intro.details[required_output[0]]['blurb']}." result += '\n */\n' else: result = '\n' if intro.member_x is None and declaration_only: result += 'static ' if has_output: # the first output arg will be used as the result cpp_type = get_cpp_type(intro.details[required_output[0]]['type'], False) spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' ' result += f'{cpp_type}{spacing}' else: result += 'void ' if not declaration_only: result += 'Image::' cplusplus_operation = operation_name if operation_name in cplusplus_keywords: cplusplus_operation += '_image' result += f'{cplusplus_operation}(' for i, name in enumerate(intro.method_args): details = intro.details[name] gtype = details['type'] cpp_type = get_cpp_type(gtype, True) spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' ' result += f'{cpp_type}{spacing}{cppize(name)}' if i != len(intro.method_args) - 1: result += ', ' # output params are passed by reference if has_output: # skip the first element for i, name in enumerate(required_output[1:]): details = intro.details[name] gtype = details['type'] cpp_type = get_cpp_type(gtype, False) spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' ' result += f'{cpp_type}{spacing}*{cppize(name)}' if i != len(required_output) - 2: result += ', ' if has_optional_options: if has_input or len(required_output) > 1: result += ', ' result += f"emscripten::val js_options{' = emscripten::val::null()' if declaration_only else ''}" result += ')' # if no 'this' available, it's a class method and they are all const if intro.member_x is not None: result += ' const' if declaration_only: result += ';' return result result += '\n{\n' if has_output: # the first output arg will be used as the result name = required_output[0] gtype = intro.details[name]['type'] cpp_type = 'VipsBlob *' if ( gtype == GValue.blob_type) else get_cpp_type(gtype, False) spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' ' result += f' {cpp_type}{spacing}{ cppize(name)};\n\n' separate_blob = False for name in intro.method_args: if intro.details[name]['type'] == GValue.blob_type: # We must take a copy of the data. result += f' VipsBlob *blob = vips_blob_copy({name}.c_str(), {name}.size());\n' separate_blob = True break if not separate_blob: result += f" {'Image::' if intro.member_x is None else 'this->'}" result += f'call("{operation_name}",' result += f"{' nullptr,' if intro.member_x is None else ''}\n" padding = ' ' * 16 if intro.member_x is None else ' ' * 15 if separate_blob: padding += ' ' * 6 result += ' Option *options = (new Option)' else: result += f'{padding}(new Option)' if intro.member_x is not None: result += f'\n {padding}->set("{intro.member_x}", *this)' all_required = intro.method_args if has_output: # first element needs to be passed by reference arg = cppize(required_output[0]) result += f'\n {padding}->set("{required_output[0]}", &{arg})' # append the remaining list all_required += required_output[1:] for name in all_required: gtype = intro.details[name]['type'] if gtype == GValue.blob_type: arg = 'blob' elif name not in required_output and get_cpp_type( gtype, True) == 'emscripten::val': type = to_snake_case(remove_prefix(type_name(gtype))).upper() arg = f'VIPS_TYPE_{type}, {cppize(name)}' # a match image is needed for image types if intro.member_x is not None \ and (gtype == GValue.image_type or gtype == GValue.array_image_type): arg += ', this' else: arg = cppize(name) result += f'\n {padding}->set("{name}", {arg})' if separate_blob: result += ';\n' result += ' vips_area_unref(VIPS_AREA(blob));\n\n' result += f" {'Image::' if intro.member_x is None else 'this->'}" result += f'call("{operation_name}",' result += f"{' nullptr,' if intro.member_x is None else ''} options, js_options);\n" else: result += f',\n{padding}js_options);\n' if has_optional_options else ');\n' if has_output: gtype = intro.details[required_output[0]]['type'] result += '\n' if gtype == GValue.blob_type: result += ' emscripten::val result = emscripten::val(emscripten::typed_memory_view(\n' result += f' VIPS_AREA({required_output[0]})->length,\n' result += f' reinterpret_cast<uint8_t *>(VIPS_AREA({required_output[0]})->data)));\n' result += f' VIPS_AREA({required_output[0]})->free_fn = nullptr;\n' result += f' vips_area_unref(VIPS_AREA({required_output[0]}));\n\n' result += ' return result;\n' else: result += f' return {required_output[0]};\n' result += '}' return result
def set(self, value): """Set a GValue. The value is converted to the type of the GValue, if possible, and assigned. """ # logger.debug('GValue.set: value = %s', value) gtype = self.gvalue.gtype fundamental = gobject_lib.g_type_fundamental(gtype) if gtype == GValue.gbool_type: gobject_lib.g_value_set_boolean(self.gvalue, value) elif gtype == GValue.gint_type: gobject_lib.g_value_set_int(self.gvalue, int(value)) elif gtype == GValue.gdouble_type: gobject_lib.g_value_set_double(self.gvalue, value) elif fundamental == GValue.genum_type: gobject_lib.g_value_set_enum(self.gvalue, GValue.to_enum(gtype, value)) elif fundamental == GValue.gflags_type: gobject_lib.g_value_set_flags(self.gvalue, value) elif gtype == GValue.gstr_type or gtype == GValue.refstr_type: gobject_lib.g_value_set_string(self.gvalue, _to_bytes(value)) elif fundamental == GValue.gobject_type: gobject_lib.g_value_set_object(self.gvalue, value.pointer) elif gtype == GValue.array_int_type: if isinstance(value, numbers.Number): value = [value] array = ffi.new('int[]', value) vips_lib.vips_value_set_array_int(self.gvalue, array, len(value)) elif gtype == GValue.array_double_type: if isinstance(value, numbers.Number): value = [value] array = ffi.new('double[]', value) vips_lib.vips_value_set_array_double(self.gvalue, array, len(value)) elif gtype == GValue.array_image_type: if isinstance(value, pyvips.Image): value = [value] vips_lib.vips_value_set_array_image(self.gvalue, len(value)) array = vips_lib.vips_value_get_array_image(self.gvalue, ffi.NULL) for i, image in enumerate(value): gobject_lib.g_object_ref(image.pointer) array[i] = image.pointer elif gtype == GValue.blob_type: # we need to set the blob to a copy of the string that vips_lib # can own memory = glib_lib.g_malloc(len(value)) ffi.memmove(memory, value, len(value)) vips_lib.vips_value_set_blob(self.gvalue, glib_lib.g_free, memory, len(value)) else: raise Error( 'unsupported gtype for set {0}, fundamental {1}'.format( type_name(gtype), type_name(fundamental)))