mirror of
https://github.com/Relintai/gdnative.git
synced 2024-11-10 00:52:11 +01:00
Added a small helper utility that can generate bindings (not automatically).
This commit is contained in:
parent
b289295a31
commit
651e06aa3f
519
scripts/core_binding_gen_helper_util.ipynb
Normal file
519
scripts/core_binding_gen_helper_util.ipynb
Normal file
@ -0,0 +1,519 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "c657926f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import ipywidgets as widgets"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "175bc029",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"lcc = widgets.Label(value=\"Current c++ class:\")\n",
|
||||
"tcc = widgets.Text()\n",
|
||||
"\n",
|
||||
"l = widgets.Label(value=\"Input:\")\n",
|
||||
"ta = widgets.Textarea(layout=widgets.Layout(width='80%', height='200px'))\n",
|
||||
"b = widgets.Button(description='Convert',tooltip='Convert',icon='check')\n",
|
||||
"\n",
|
||||
"ol = widgets.Label(value=\"Output:\")\n",
|
||||
"\n",
|
||||
"olh = widgets.Label(value=\"Header:\")\n",
|
||||
"otah = widgets.Output(layout={'border': '1px solid black'})\n",
|
||||
"\n",
|
||||
"ols = widgets.Label(value=\"Source:\")\n",
|
||||
"otas = widgets.Output(layout={'border': '1px solid black'})\n",
|
||||
"\n",
|
||||
"ool = widgets.Label(value=\"Output:\")\n",
|
||||
"out = widgets.Output(layout={'border': '1px solid black'})\n",
|
||||
"\n",
|
||||
"vbi = widgets.VBox([ lcc, tcc, l, ta, b])\n",
|
||||
"vbo = widgets.VBox([ol, olh, otah, ols, otas, ool, out])\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "ddbd7a11",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "NameError",
|
||||
"evalue": "name 'asdsd' is not defined",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
|
||||
"Input \u001b[0;32mIn [4]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtransform_input\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mglobals\u001b[39m():\n\u001b[1;32m 2\u001b[0m b\u001b[38;5;241m.\u001b[39mon_click(transform_input, \u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[0;32m----> 3\u001b[0m \u001b[43masdsd\u001b[49m()\n\u001b[1;32m 5\u001b[0m class_remaps \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 6\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mvoid\u001b[39m\u001b[38;5;124m'\u001b[39m: \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mvoid\u001b[39m\u001b[38;5;124m'\u001b[39m,\n\u001b[1;32m 7\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mVariant\u001b[39m\u001b[38;5;124m'\u001b[39m: \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mpandemonium_variant\u001b[39m\u001b[38;5;124m'\u001b[39m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 44\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mPoolColorArray\u001b[39m\u001b[38;5;124m'\u001b[39m: \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mpandemonium_pool_color_array\u001b[39m\u001b[38;5;124m'\u001b[39m,\n\u001b[1;32m 45\u001b[0m }\n\u001b[1;32m 47\u001b[0m class_c_no_force_ptr \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 48\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbool\u001b[39m\u001b[38;5;124m'\u001b[39m: \u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 49\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mint\u001b[39m\u001b[38;5;124m'\u001b[39m: \u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 50\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mreal_t\u001b[39m\u001b[38;5;124m'\u001b[39m: \u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 51\u001b[0m }\n",
|
||||
"\u001b[0;31mNameError\u001b[0m: name 'asdsd' is not defined"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"if 'transform_input' in globals():\n",
|
||||
" b.on_click(transform_input, True)\n",
|
||||
"\n",
|
||||
"class_remaps = {\n",
|
||||
" 'void': 'void',\n",
|
||||
" 'Variant': 'pandemonium_variant',\n",
|
||||
" 'bool': 'pandemonium_bool',\n",
|
||||
" 'int': 'pandemonium_int',\n",
|
||||
" 'real_t': 'pandemonium_real',\n",
|
||||
" 'String': 'pandemonium_string',\n",
|
||||
" 'Rect2': 'pandemonium_rect2',\n",
|
||||
" 'Rect2i': 'pandemonium_rect2i',\n",
|
||||
" 'Vector2': 'pandemonium_vector2',\n",
|
||||
" 'Vector2i': 'pandemonium_vector2i',\n",
|
||||
" 'Vector3': 'pandemonium_vector3',\n",
|
||||
" 'Vector3i': 'pandemonium_vector3i',\n",
|
||||
" 'Vector4': 'pandemonium_vector4',\n",
|
||||
" 'Vector4i': 'pandemonium_vector4i',\n",
|
||||
" 'Plane': 'pandemonium_plane',\n",
|
||||
" 'Quaternion': 'pandemonium_quaternion',\n",
|
||||
" 'AABB': 'pandemonium_aabb',\n",
|
||||
" 'Basis': 'pandemonium_basis',\n",
|
||||
" 'Transform': 'pandemonium_transform',\n",
|
||||
" 'Transform2d': 'pandemonium_transform2d',\n",
|
||||
" 'Projection': 'pandemonium_projection',\n",
|
||||
" 'Color': 'pandemonium_color',\n",
|
||||
" 'NodePath': 'pandemonium_node_path',\n",
|
||||
" 'RID': 'pandemonium_rid',\n",
|
||||
" 'Object': 'pandemonium_object',\n",
|
||||
" 'StringName': 'pandemonium_string_name',\n",
|
||||
" 'Dictionary': 'pandemonium_dictionary',\n",
|
||||
" 'Array': 'pandemonium_array',\n",
|
||||
" 'PoolByteArray': 'pandemonium_pool_byte_array',\n",
|
||||
" 'PoolIntArray': 'pandemonium_pool_int_array',\n",
|
||||
" 'PoolRealArray': 'pandemonium_pool_real_array',\n",
|
||||
" 'PoolStringArray': 'pandemonium_pool_string_array',\n",
|
||||
" 'PoolVector2Array': 'pandemonium_pool_vector2_array',\n",
|
||||
" 'PoolVector2iArray': 'pandemonium_pool_vector2i_array',\n",
|
||||
" 'PoolVector3Array': 'pandemonium_pool_vector3_array',\n",
|
||||
" 'PoolVector3iArray': 'pandemonium_pool_vector3i_array',\n",
|
||||
" 'PoolVector4Array': 'pandemonium_pool_vector4_array',\n",
|
||||
" 'PoolVector4iArray': 'pandemonium_pool_vector4i_array',\n",
|
||||
" 'PoolColorArray': 'pandemonium_pool_color_array',\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"class_c_no_force_ptr = {\n",
|
||||
" 'bool': True,\n",
|
||||
" 'int': True,\n",
|
||||
" 'real_t': True,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"METHOD_ARG_TYPE_NONE = 0\n",
|
||||
"METHOD_ARG_TYPE_REFERENCE = 1\n",
|
||||
"METHOD_ARG_TYPE_POINTER = 2\n",
|
||||
"\n",
|
||||
"def get_current_cpp_class():\n",
|
||||
" return tcc.value\n",
|
||||
"\n",
|
||||
"def get_current_c_class():\n",
|
||||
" return class_remaps[tcc.value]\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"class PMethodArg:\n",
|
||||
" def __init__(self):\n",
|
||||
" self.is_const = False\n",
|
||||
" self.cpp_type = ''\n",
|
||||
" self.cpp_arg_type = METHOD_ARG_TYPE_NONE\n",
|
||||
" self.cpp_has_default = False\n",
|
||||
" self.arg_name = ''\n",
|
||||
" self.cpp_default_value = ''\n",
|
||||
" \n",
|
||||
" def is_no_force_ptr(self):\n",
|
||||
" return self.cpp_type in class_c_no_force_ptr\n",
|
||||
" \n",
|
||||
" def get_arg_name_local(self):\n",
|
||||
" return self.arg_name[2:]\n",
|
||||
" \n",
|
||||
" def generate_c_arg(self):\n",
|
||||
" s = ', '\n",
|
||||
" \n",
|
||||
" if self.is_const:\n",
|
||||
" s += 'const '\n",
|
||||
" \n",
|
||||
" s += class_remaps[self.cpp_type] + ' '\n",
|
||||
" \n",
|
||||
" is_pointer = self.cpp_arg_type != METHOD_ARG_TYPE_NONE\n",
|
||||
" \n",
|
||||
" if is_pointer and self.cpp_type in class_c_no_force_ptr:\n",
|
||||
" is_pointer = False\n",
|
||||
" \n",
|
||||
" if is_pointer:\n",
|
||||
" s += '*'\n",
|
||||
" \n",
|
||||
" s += self.arg_name\n",
|
||||
" \n",
|
||||
" return s\n",
|
||||
" \n",
|
||||
" def generate_convert_line(self):\n",
|
||||
" s = ''\n",
|
||||
"\n",
|
||||
" if self.is_const:\n",
|
||||
" s += 'const '\n",
|
||||
" \n",
|
||||
" if self.is_no_force_ptr():\n",
|
||||
" s += self.cpp_type + ' ' + self.get_arg_name_local() + ' = ' + self.arg_name + ';'\n",
|
||||
" else:\n",
|
||||
" s += self.cpp_type + ' *' + self.get_arg_name_local() + ' = ('\n",
|
||||
" \n",
|
||||
" if self.is_const:\n",
|
||||
" s += 'const '\n",
|
||||
" \n",
|
||||
" s += self.cpp_type + ' *)' + self.arg_name + ';'\n",
|
||||
" \n",
|
||||
" return s\n",
|
||||
" \n",
|
||||
" def parse_line(self, l):\n",
|
||||
" self.cpp_has_default = False\n",
|
||||
" \n",
|
||||
" if '=' in l:\n",
|
||||
" sl = l.split('=')\n",
|
||||
" \n",
|
||||
" l = sl[0].strip()\n",
|
||||
" self.cpp_has_default = True\n",
|
||||
" self.cpp_default_value = sl[1].strip()\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" self.cpp_arg_type = METHOD_ARG_TYPE_NONE\n",
|
||||
" \n",
|
||||
" if '*' in l:\n",
|
||||
" self.cpp_arg_type = METHOD_ARG_TYPE_POINTER\n",
|
||||
" \n",
|
||||
" if '&' in l:\n",
|
||||
" self.cpp_arg_type = METHOD_ARG_TYPE_REFERENCE\n",
|
||||
" \n",
|
||||
" l = l.replace('*', '')\n",
|
||||
" l = l.replace('&', '')\n",
|
||||
" \n",
|
||||
" spls = l.split(' ')\n",
|
||||
" \n",
|
||||
" self.is_const = spls[0].strip() == 'const'\n",
|
||||
" \n",
|
||||
" if self.is_const:\n",
|
||||
" spls = spls[1:]\n",
|
||||
" \n",
|
||||
" with out:\n",
|
||||
" print(spls)\n",
|
||||
" \n",
|
||||
" self.cpp_type = spls[0]\n",
|
||||
" self.arg_name = spls[1]\n",
|
||||
" \n",
|
||||
" def __str__(self):\n",
|
||||
" s = ' PMethodArg:\\n'\n",
|
||||
" \n",
|
||||
" s += ' is_const: ' + str(self.is_const) + '\\n'\n",
|
||||
" s += ' cpp_type: ' + self.cpp_type + '\\n'\n",
|
||||
" s += ' cpp_arg_type: ' + str(self.cpp_arg_type) + '\\n'\n",
|
||||
" s += ' arg_name: ' + self.arg_name + '\\n'\n",
|
||||
" s += ' cpp_has_default: ' + str(self.cpp_has_default) + '\\n'\n",
|
||||
" s += ' cpp_default_value: ' + self.cpp_default_value + '\\n'\n",
|
||||
" \n",
|
||||
" return s\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class PMethod:\n",
|
||||
" def __init__(self):\n",
|
||||
" self.args = []\n",
|
||||
" \n",
|
||||
" self.method_name = ''\n",
|
||||
"\n",
|
||||
" self.cpp_return_type = ''\n",
|
||||
" self.is_const = False\n",
|
||||
" self.is_constructor = False\n",
|
||||
" \n",
|
||||
" def parse_line(self, l):\n",
|
||||
" l = l.replace('//', '')\n",
|
||||
" l = l.replace(';', '')\n",
|
||||
" l = l.replace('inline', '')\n",
|
||||
" l = l.replace('_FORCE_INLINE_', '')\n",
|
||||
" l = l.strip()\n",
|
||||
" \n",
|
||||
" self.is_const = False\n",
|
||||
" \n",
|
||||
" if l.endswith('const'):\n",
|
||||
" self.is_const = True\n",
|
||||
" \n",
|
||||
" #remove it from the string\n",
|
||||
" \n",
|
||||
" l = l[:-5]\n",
|
||||
" \n",
|
||||
" l = l.replace(')', '')\n",
|
||||
" \n",
|
||||
" sps = l.split('(')\n",
|
||||
" \n",
|
||||
" mdeffull = sps[0].strip()\n",
|
||||
" margsfull = sps[1].strip()\n",
|
||||
" \n",
|
||||
" mds = mdeffull.split(' ')\n",
|
||||
" \n",
|
||||
" self.is_constructor = False\n",
|
||||
" \n",
|
||||
" mrettype = ''\n",
|
||||
" mdef = ''\n",
|
||||
" \n",
|
||||
" if len(mds) == 1:\n",
|
||||
" self.is_constructor = True\n",
|
||||
" self.method_name = mds[0].strip()\n",
|
||||
" else:\n",
|
||||
" self.cpp_return_type = mds[0].strip()\n",
|
||||
" self.method_name = mds[1].strip()\n",
|
||||
" \n",
|
||||
" margsspl = margsfull.split(',')\n",
|
||||
" \n",
|
||||
" for ml in margsspl:\n",
|
||||
" ml = ml.strip()\n",
|
||||
" \n",
|
||||
" if ml == '':\n",
|
||||
" continue\n",
|
||||
" \n",
|
||||
" a = PMethodArg()\n",
|
||||
" a.parse_line(ml)\n",
|
||||
" self.args.append(a)\n",
|
||||
" \n",
|
||||
" def get_c_return_type(self):\n",
|
||||
" return class_remaps[self.cpp_return_type]\n",
|
||||
" \n",
|
||||
" def get_c_method_name(self):\n",
|
||||
" return get_current_c_class() + '_' + self.method_name\n",
|
||||
"\n",
|
||||
" def generate_method_string(self):\n",
|
||||
" s = ''\n",
|
||||
" \n",
|
||||
" s += self.get_c_return_type() + ' GDAPI ' + self.get_c_method_name() + '('\n",
|
||||
" \n",
|
||||
" if self.is_const:\n",
|
||||
" s += 'const '\n",
|
||||
" \n",
|
||||
" s += get_current_c_class() + ' *p_self'\n",
|
||||
" \n",
|
||||
" for a in self.args:\n",
|
||||
" s += a.generate_c_arg()\n",
|
||||
" \n",
|
||||
" s += ')'\n",
|
||||
" \n",
|
||||
" return s\n",
|
||||
" \n",
|
||||
" def generate_header_string(self):\n",
|
||||
" return self.generate_method_string() + ';'\n",
|
||||
" \n",
|
||||
" def generate_impl_string(self):\n",
|
||||
" s = self.generate_method_string() + '{\\n'\n",
|
||||
" \n",
|
||||
" # Create dest variable at top if needed\n",
|
||||
" return_needs_conversion = False\n",
|
||||
" \n",
|
||||
" if self.cpp_return_type != 'void':\n",
|
||||
" if not self.cpp_return_type in class_c_no_force_ptr:\n",
|
||||
" return_needs_conversion = True\n",
|
||||
" \n",
|
||||
" if return_needs_conversion:\n",
|
||||
" # it needs to be explicitly converted\n",
|
||||
" # Just Add dest var here\n",
|
||||
" s += '\\t' + self.get_c_return_type() + ' dest;\\n'\n",
|
||||
" \n",
|
||||
" s += '\\t'\n",
|
||||
" \n",
|
||||
" # Create self local\n",
|
||||
" if self.is_const:\n",
|
||||
" s += 'const '\n",
|
||||
" \n",
|
||||
" s += get_current_cpp_class() + '* self = ('\n",
|
||||
" \n",
|
||||
" if self.is_const:\n",
|
||||
" s += 'const '\n",
|
||||
" \n",
|
||||
" s += get_current_cpp_class() + ' *)p_self;\\n'\n",
|
||||
" \n",
|
||||
" # Create param locals\n",
|
||||
" self_call_args = []\n",
|
||||
" \n",
|
||||
" for a in self.args:\n",
|
||||
" if a.is_no_force_ptr():\n",
|
||||
" self_call_args.append(a.arg_name)\n",
|
||||
" else:\n",
|
||||
" self_call_args.append('*' + a.get_arg_name_local())\n",
|
||||
" \n",
|
||||
" s += '\\t' + a.generate_convert_line() + '\\n'\n",
|
||||
" \n",
|
||||
" # Create method call\n",
|
||||
" s += '\\t'\n",
|
||||
"\n",
|
||||
" # We have a return type\n",
|
||||
" if self.cpp_return_type != 'void':\n",
|
||||
" if return_needs_conversion:\n",
|
||||
" s += '*((' + self.cpp_return_type + ' *)&dest) = '\n",
|
||||
" else:\n",
|
||||
" s += 'return '\n",
|
||||
"\n",
|
||||
" s += 'self->' + self.method_name + '('\n",
|
||||
" \n",
|
||||
" first = True\n",
|
||||
" for a in self_call_args:\n",
|
||||
" if first:\n",
|
||||
" first = False\n",
|
||||
" s += a\n",
|
||||
" else:\n",
|
||||
" s += ', ' + a\n",
|
||||
" \n",
|
||||
" s += ');\\n'\n",
|
||||
" \n",
|
||||
" if self.cpp_return_type != 'void':\n",
|
||||
" s += '\\treturn dest;\\n'\n",
|
||||
" \n",
|
||||
" s += '}'\n",
|
||||
" \n",
|
||||
" return s\n",
|
||||
" \n",
|
||||
" def __str__(self):\n",
|
||||
" s = 'PMethod:\\n'\n",
|
||||
" \n",
|
||||
" s += ' method_name: ' + self.method_name + '\\n'\n",
|
||||
" s += ' cpp_return_type: ' + self.cpp_return_type + '\\n'\n",
|
||||
" s += ' is_const: ' + str(self.is_const) + '\\n'\n",
|
||||
" s += ' is_constructor: ' + str(self.is_constructor) + '\\n'\n",
|
||||
" \n",
|
||||
" s += ' args: [\\n'\n",
|
||||
" \n",
|
||||
" for a in self.args:\n",
|
||||
" s += str(a)\n",
|
||||
" s += '\\n'\n",
|
||||
" \n",
|
||||
" s += ' ]'\n",
|
||||
" \n",
|
||||
" return s\n",
|
||||
"\n",
|
||||
"def transform_input(b):\n",
|
||||
" with out:\n",
|
||||
" out.clear_output()\n",
|
||||
" \n",
|
||||
" with otah:\n",
|
||||
" otah.clear_output()\n",
|
||||
" \n",
|
||||
" with otas:\n",
|
||||
" otas.clear_output()\n",
|
||||
" \n",
|
||||
" lines = ta.value.split('\\n')\n",
|
||||
" \n",
|
||||
" for l in lines:\n",
|
||||
" if l.strip() == '':\n",
|
||||
" with otah:\n",
|
||||
" print('')\n",
|
||||
" \n",
|
||||
" with otas:\n",
|
||||
" print('')\n",
|
||||
" \n",
|
||||
" continue\n",
|
||||
" \n",
|
||||
" with out:\n",
|
||||
" print('line:')\n",
|
||||
" print(l)\n",
|
||||
" print('')\n",
|
||||
" \n",
|
||||
" method = PMethod()\n",
|
||||
" method.parse_line(l)\n",
|
||||
" \n",
|
||||
" with otah:\n",
|
||||
" print(method.generate_header_string())\n",
|
||||
" \n",
|
||||
" with otas:\n",
|
||||
" print(method.generate_impl_string())\n",
|
||||
" \n",
|
||||
" with out:\n",
|
||||
" print(str(method))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"b.on_click(transform_input)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3060f92f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Input"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1b472401",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"display(vbi)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "89053f5a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Output"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5a7057b5",
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"display(vbo)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "37dfae8d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d86d6156",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" "
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
Loading…
Reference in New Issue
Block a user