{ "info": { "author": "Ryan Parry-Jones", "author_email": "ryanspj+github@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Manufacturing", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3 :: Only" ], "description": "# uStubby\n\nuStubby is a library for generating micropython c extension stubs from type annotated python.\n\nAccording to [Link](https://micropython.org/)\n\n\"MicroPython is a lean and efficient implementation of the Python 3 programming language that includes a small subset of the Python standard library and is optimised to run on microcontrollers and in constrained environments.\"\n\nSometimes, pure python performance isn't enough.\nC extensions are a way to improve performace whilst still having the bulk of your code in micropython.\n\nUnfortunately there is a lot of boilerplate code needed to build these extensions. \n\nuStubby is designed to make this process as easy as writing python.\n\n## Getting Started\n\nuStubby is targeted to run on Python 3.7, but should run on versions 3.6 or greater\n\n### Installing\n\nCurrently, there are no external dependencies for running uStubby.\nClone the repository using git and just put it on the path to install.\nAlternatively, install from PyPI with \n```bash\npip install ustubby\n```\n\n## Usage\nThis example follows generating the template as shown [here](http://docs.micropython.org/en/latest/develop/cmodules.html#basic-example)\n\nCreate a python file with the module as you intend to use it from micropython.\n\neg. example.py\n```python\ndef add_ints(a: int, b: int) -> int:\n \"\"\"Adds two integers\n :param a:\n :param b:\n :return:a + b\"\"\"\n```\nWe can then convert this into the appropriate c stub by running\n```python\nimport ustubby\nimport example\n\nprint(ustubby.stub_module(example))\n```\n
Output

\n\n```c\n// Include required definitions first.\n#include \"py/obj.h\"\n#include \"py/runtime.h\"\n#include \"py/builtin.h\"\n\n//Adds two integers\n//:param a:\n//:param b:\n//:return:a + b\nSTATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {\n mp_int_t a = mp_obj_get_int(a_obj);\n mp_int_t b = mp_obj_get_int(b_obj);\n mp_int_t ret_val;\n\n //Your code here\n\n return mp_obj_new_int(ret_val);\n}\nMP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);\n\nSTATIC const mp_rom_map_elem_t example_module_globals_table[] = {\n\t{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example) },\n\t{ MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },\n};\n\nSTATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);\nconst mp_obj_module_t example_user_cmodule = {\n\t.base = {&mp_type_module},\n\t.globals = (mp_obj_dict_t*)&example_module_globals,\n};\n\nMP_REGISTER_MODULE(MP_QSTR_example, example_user_cmodule, MODULE_EXAMPLE_ENABLED);\n```\n

\n\nThis will parse all the functions in the module and attach them to the same namespace in micropython.\n##### Note: It will only generate the boilerplate code and not the actual code that does the work such as a + b\nAfter editing the code in the template at the place marked //Code goes here you can follow the instructions \n[here](http://docs.micropython.org/en/latest/develop/cmodules.html#basic-example) for modifying \nthe Make File and building the module into your micro python deployment.\n\nYou should then be able to use the module in micro python by typing\n```python\nimport example # from example.c compiled into micropython\nexample.add_ints(1, 2)\n# prints 3\n```\n##### Note: This example.py is the one compiled into the micropython source and not the file we created earlier\n\n### Advanced usage\nIf you added two more functions to the original example.py\n```python\ndef lots_of_parameters(a: int, b: float, c: tuple, d: object, e: str) -> None:\n \"\"\"\n :param a:\n :param b:\n :param c:\n :param d:\n :return:\n \"\"\"\n\ndef readfrom_mem(addr: int = 0, memaddr: int = 0, arg: object = None, *, addrsize: int = 8) -> str:\n \"\"\"\n :param addr:\n :param memaddr:\n :param arg:\n :param addrsize: Keyword only arg\n :return:\n \"\"\"\n```\n\nlogs_of_parameters shows the types of types you can parse in. You always need to annotate each parameter and the return.\nreadfrom_mem shows that you can set default values for certain parameters and specify that addrsize is a keyword only\nargument.\n\nAt the c level in micropython, there is only three ways of implementing a function.\n##### Basic Case\n```python\ndef foo(a, b, c): # 0 to 3 args\n pass\n```\n```c\nMP_DEFINE_CONST_FUN_OBJ_X // Where x is 0 to 3 args\n```\n##### Greater than three positional args\n```python\ndef foo(*args):\n pass\n```\n```c\nMP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN\n```\n##### Arbitary args\n```python\ndef foo(*args, **kwargs):\n pass\n```\n```c\nMP_DEFINE_CONST_FUN_OBJ_KW\n```\nEach successively increasing the boiler plate to conveniently accessing the variables.\nUsing the same code to parse it\n```python\nimport ustubby\nimport example\n\nprint(ustubby.stub_module(example))\n```\n
Output

\n\n```c\n// Include required definitions first.\n#include \"py/obj.h\"\n#include \"py/runtime.h\"\n#include \"py/builtin.h\"\n\n//Adds two integers\n//:param a:\n//:param b:\n//:return:a + b\nSTATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {\n mp_int_t a = mp_obj_get_int(a_obj);\n mp_int_t b = mp_obj_get_int(b_obj);\n mp_int_t ret_val;\n\n //Your code here\n\n return mp_obj_new_int(ret_val);\n}\nMP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);\n//\n//:param a:\n//:param b:\n//:param c:\n//:param d:\n//:return:\n//\nSTATIC mp_obj_t example_lots_of_parameters(size_t n_args, const mp_obj_t *args) {\n mp_int_t a = mp_obj_get_int(a_obj);\n mp_float_t b = mp_obj_get_float(b_obj);\n mp_obj_t *c = NULL;\n size_t c_len = 0;\n mp_obj_get_array(c_arg, &c_len, &c);\n mp_obj_t d args[ARG_d].u_obj;\n\n //Your code here\n\n return mp_const_none;\n}\nMP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(example_lots_of_parameters_obj, 4, 4, example_lots_of_parameters);\n//\n//:param addr:\n//:param memaddr:\n//:param arg:\n//:param addrsize: Keyword only arg\n//:return:\n//\nSTATIC mp_obj_t example_readfrom_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {\n enum { ARG_addr, ARG_memaddr, ARG_arg, ARG_addrsize };\n STATIC const mp_arg_t example_readfrom_mem_allowed_args[] = {\n { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, { .u_int = 0 } },\n { MP_QSTR_memaddr, MP_ARG_REQUIRED | MP_ARG_INT, { .u_int = 0 } },\n { MP_QSTR_arg, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },\n { MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 8 } },\n };\n\n mp_arg_val_t args[MP_ARRAY_SIZE(example_readfrom_mem_allowed_args)];\n mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,\n MP_ARRAY_SIZE(example_readfrom_mem_allowed_args), example_readfrom_mem_allowed_args, args);\n\n mp_int_t addr = args[ARG_addr].u_int;\n mp_int_t memaddr = args[ARG_memaddr].u_int;\n mp_obj_t arg = args[ARG_arg].u_obj;\n mp_int_t addrsize = args[ARG_addrsize].u_int;\n\n //Your code here\n\n return mp_obj_new_str(, );\n}\nMP_DEFINE_CONST_FUN_OBJ_KW(example_readfrom_mem_obj, 1, example_readfrom_mem);\n\nSTATIC const mp_rom_map_elem_t example_module_globals_table[] = {\n\t{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example) },\n\t{ MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },\n\t{ MP_ROM_QSTR(MP_QSTR_lots_of_parameters), MP_ROM_PTR(&example_lots_of_parameters_obj) },\n\t{ MP_ROM_QSTR(MP_QSTR_readfrom_mem), MP_ROM_PTR(&example_readfrom_mem_obj) },\n};\n\nSTATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);\nconst mp_obj_module_t example_user_cmodule = {\n\t.base = {&mp_type_module},\n\t.globals = (mp_obj_dict_t*)&example_module_globals,\n};\n\nMP_REGISTER_MODULE(MP_QSTR_example, example_user_cmodule, MODULE_EXAMPLE_ENABLED);\n```\n

\n\n#### Adding fully implemented c functions\nGoing one step further you can directly add c code to be substituted into the c generated code where the \n\"//Your code here comment\" is.\n\nFor example, starting with a fresh example.py you could define it as.\n\n```python\ndef add_ints(a: int, b: int) -> int:\n \"\"\"Adds two integers\n :param a:\n :param b:\n :return:a + b\"\"\"\nadd_ints.code = \" ret_val = a + b;\"\n```\nto get a fully defined function in c\n\n
Output

\n\n```c\n// Include required definitions first.\n#include \"py/obj.h\"\n#include \"py/runtime.h\"\n#include \"py/builtin.h\"\n\n//Adds two integers\n//:param a:\n//:param b:\n//:return:a + b\nSTATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {\n mp_int_t a = mp_obj_get_int(a_obj);\n mp_int_t b = mp_obj_get_int(b_obj);\n mp_int_t ret_val;\n\n ret_val = a + b;\n\n return mp_obj_new_int(ret_val);\n}\nMP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);\n\nSTATIC const mp_rom_map_elem_t example_module_globals_table[] = {\n\t{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example) },\n\t{ MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },\n};\n\nSTATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);\nconst mp_obj_module_t example_user_cmodule = {\n\t.base = {&mp_type_module},\n\t.globals = (mp_obj_dict_t*)&example_module_globals,\n};\n\nMP_REGISTER_MODULE(MP_QSTR_example, example_user_cmodule, MODULE_EXAMPLE_ENABLED);\n```\n

\n\n#### Using functions without a module definition\nIf you don't need the fully module boiler plate, you can generate individual functions with\n```python\nimport ustubby\ndef add_ints(a: int, b: int) -> int:\n \"\"\"add two ints\"\"\"\nadd_ints.code = \" ret_val = a + b;\"\nadd_ints.__module__ = \"new_module\"\n\nprint(ustubby.stub_function(add_ints))\n```\n\n```c\n//add two ints\nSTATIC mp_obj_t new_module_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {\n mp_int_t a = mp_obj_get_int(a_obj);\n mp_int_t b = mp_obj_get_int(b_obj);\n mp_int_t ret_val;\n\n ret_val = a + b;\n\n return mp_obj_new_int(ret_val);\n}\nMP_DEFINE_CONST_FUN_OBJ_2(new_module_add_ints_obj, new_module_add_ints);\n```\n\n#### Parsing Litex Files\nuStubby is also trying to support c code generation from Litex files such as\n```csv\n#--------------------------------------------------------------------------------\n# Auto-generated by Migen (5585912) & LiteX (e637aa65) on 2019-08-04 03:04:29\n#--------------------------------------------------------------------------------\ncsr_register,cas_leds_out,0x82000800,1,rw\ncsr_register,cas_buttons_ev_status,0x82000804,1,rw\ncsr_register,cas_buttons_ev_pending,0x82000808,1,rw\ncsr_register,cas_buttons_ev_enable,0x8200080c,1,rw\ncsr_register,ctrl_reset,0x82001000,1,rw\ncsr_register,ctrl_scratch,0x82001004,4,rw\ncsr_register,ctrl_bus_errors,0x82001014,4,ro\n```\nCurrently only csr_register is supported. Please raise issues if you need to expand this feature.\n```python\nimport ustubby\nmod = ustubby.parse_csv(\"csr.csv\")\nprint(ustubby.stub_module(mod))\n```\n\n## Running the tests\nInstall the test requirements with \n```bash\npip install -r requirements-test.txt\n```\nInstall the package in editable mode\n```bash\npip install -e .\n```\nRun the tests\n```bash\npytest\n```\n\n## Check out the docs\n\nTBD\n\n## Contributing\n\nContributions are welcome. Get in touch or create a new pull request.\n\n## Credits\nInspired by \n- [Extending MicroPython: Using C for Good](https://youtu.be/fUb3Urw4H-E)\n- [Online C Stub Generator](https://gitlab.com/oliver.robson/mpy-c-stub-gen)\n- [Micropython](https://micropython.org) \n\nPyCon AU 2019 Sprints\n\n## Authors\n\n* **Ryan Parry-Jones** - *Original Developer* - [pazzarpj](https://github.com/pazzarpj)\n\nSee also the list of [contributors](https://github.com/pazzarpj/micropython-ustubby/contributors) who participated in this project.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/pazzarpj/micropython-ustubby", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "ustubby", "package_url": "https://pypi.org/project/ustubby/", "platform": "", "project_url": "https://pypi.org/project/ustubby/", "project_urls": { "Homepage": "https://github.com/pazzarpj/micropython-ustubby" }, "release_url": "https://pypi.org/project/ustubby/0.1.1/", "requires_dist": null, "requires_python": ">=3.6", "summary": "Micropython c stub generator", "version": "0.1.1" }, "last_serial": 5648530, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "a502a161ed6c8db784de5a4da4a7c567", "sha256": "cb49dec3073d29fd938ec87bc0cacbc734c5f40cc62435d2eb2ce4e5ef22addc" }, "downloads": -1, "filename": "ustubby-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "a502a161ed6c8db784de5a4da4a7c567", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 5653, "upload_time": "2019-08-06T13:03:36", "url": "https://files.pythonhosted.org/packages/79/0f/46a2e0a35cb701353bc2d6cda44dea74e58c4025765cbdeda6a75c582f5b/ustubby-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3ed11df315237238e84ac2f004ffdc38", "sha256": "e2f53e251d6772b9c3600c0d5c62613086c519d4aebdfd1a06b10281cd85e6e7" }, "downloads": -1, "filename": "ustubby-0.1.0.tar.gz", "has_sig": false, "md5_digest": "3ed11df315237238e84ac2f004ffdc38", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 7433, "upload_time": "2019-08-06T13:03:38", "url": "https://files.pythonhosted.org/packages/e4/4f/4bd49f7ba9b3d4b17e7dc8e9f32f585b727ea550df32b715d9405ff1670b/ustubby-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "9cdf8fd8ccc653013b8c908d84d0fa23", "sha256": "3f5ff52526084267c504522fb31541a8d4894bb3c39b3364083528abdb33b080" }, "downloads": -1, "filename": "ustubby-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "9cdf8fd8ccc653013b8c908d84d0fa23", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 9048, "upload_time": "2019-08-08T07:21:57", "url": "https://files.pythonhosted.org/packages/cb/8a/07489b1736dce07ea880dddbb76518211aa95e8049f0b42be7b3db2df051/ustubby-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3f2867a301a31ee5f9d78b826d35f7f7", "sha256": "fde8002e23cf4a12bdcaec11362cb6579e1725dfad63bf2af1f8532bb2fd3d65" }, "downloads": -1, "filename": "ustubby-0.1.1.tar.gz", "has_sig": false, "md5_digest": "3f2867a301a31ee5f9d78b826d35f7f7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 9752, "upload_time": "2019-08-08T07:21:59", "url": "https://files.pythonhosted.org/packages/bc/33/32bbf82ac1f769a9dbd1d37c19d71785d0e42af65ed96695f5254964f10f/ustubby-0.1.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "9cdf8fd8ccc653013b8c908d84d0fa23", "sha256": "3f5ff52526084267c504522fb31541a8d4894bb3c39b3364083528abdb33b080" }, "downloads": -1, "filename": "ustubby-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "9cdf8fd8ccc653013b8c908d84d0fa23", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 9048, "upload_time": "2019-08-08T07:21:57", "url": "https://files.pythonhosted.org/packages/cb/8a/07489b1736dce07ea880dddbb76518211aa95e8049f0b42be7b3db2df051/ustubby-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3f2867a301a31ee5f9d78b826d35f7f7", "sha256": "fde8002e23cf4a12bdcaec11362cb6579e1725dfad63bf2af1f8532bb2fd3d65" }, "downloads": -1, "filename": "ustubby-0.1.1.tar.gz", "has_sig": false, "md5_digest": "3f2867a301a31ee5f9d78b826d35f7f7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 9752, "upload_time": "2019-08-08T07:21:59", "url": "https://files.pythonhosted.org/packages/bc/33/32bbf82ac1f769a9dbd1d37c19d71785d0e42af65ed96695f5254964f10f/ustubby-0.1.1.tar.gz" } ] }